Tags:
create new tag
, view all tags
-- PedroRio - 17 Feb 2011

XEO Web Components - Custom Component Creation

XEO comes bundled with a set of predefined components to help you deal with various situations, but there will always be a situation where you need something so specific that you'll have to create your own custom component.

To create and user a XEO Web Component you need to perform three steps (which will be explained in detail bellow):

  • Create an Implementation class
  • Create a Renderer Class
  • Register the component in the project

_

Implementation Class

To create a new Custom XEO Web Component you'll need to define an implementation class which will be responsible for declaring the component properties and the component's logic. A custom component must extend the netgest.bo.xwc.framework.components.XUIComponentBase class (or any class that extends from that one, including XEO's own components).

When creating an implementation class you declare the set of properties of the components as class variables (of a particular type) and you can redefine some of the methods that are responsible for component logic (inherited from the XUIComponent Base Class). Read the following paragraphs in order to know how to declare component properties.

Component Properties

Component properties allow you to configure a given component with a certain value (for a certain attribute it had), as such they are extremely useful when creating reusable components. The value of a property can be either supplied directly in the xml definition a viewer, or binded to a java bean property. Also, properties can have a notion of state (remembering their previous values). There are four different types of properties (properties are typed):

When declaring properties you'll also need to declare their data type (any type is valid). bellow is an example of declaring a property named text (of type String) and "bindable" to a Java bean.

private XUIBindProperty<String> text = 
        new XUIBindProperty<String>("text", this, String.class);

Notice the use of generics when declaring the property, but also the last parameter of the constructor is the class of the parameter type (this is required). The first parameter is the name of property as a string and the second parameter is the component itself. You could also use another constructor which allows you to supply a default value, like the following:

private XUIBindProperty<String> text = 
        new XUIBindProperty<String>("text", this, String.class, "Default Text");

If declaring a simple property without any binding to the bean you can do the following:

private XUIBaseProperty<String> other = 
        new XUIBaseProperty<String>("other", this);

When declaring a property, you must also declare a getter and a setter like the following (the example is valid for a property not binded to a Java bean property):

public String getOther(){
        return other.getValue();
}
    
public void setOther(String newValue){
        other.setValue(newValue);
}

and like the following if the property is binded to a Java bean property.

public void setText( String textExpr ) {
        this.text.setExpressionText(textExpr);
    }
    
public String getText() {
        return this.text.getEvaluatedValue();
}

_

Component Behavior

Inherited from the XUIComponentBase class are some methods that can be overridden to implement specific behavior of the custom component, like the following:

  • preRender - Method that's executed right before the component is rendered to the client and allows you to make modifications to the component after the initialization.
  • initComponent - Method that's executed after the component is created, it's used to perform the component initialization.
  • wasStateChanged - This method is invoked by the framework to determine whether the component should be updated client-side (browser). If it returns true the component will be updated on the client and if it returns false it will let the component decide for itself.

Renderer Class

The renderer class is the class that's responsible for generating the HTML/CSS/Javascript necessary to render the component inside a viewer. Each renderer class must extend the netgest.bo.xwc.framework.XUIRenderer class and override a few methods in order to work. One very important step when creating the renderer of a custom component is to create a placeholder for the component. The placeholder should be an html div element having as identifier the identifier of the component (component identifiers are generated automatically if not declared in the xml definition).This is necessary so that when a component is updated XEO knows where to replace/update the content of your component.

XEO's Renderer classes are usually declared as inner classes of the implementation class but they can be declared in a separate class as well. If declared inside the implementation class the should be declared as follows:

public static class XEOHTMLRenderer extends XUIRenderer { }

The renderer class can have any other name, just be sure that when registering the component you use the same name (if using XEO Studio's Web Component Wizard, this step is not necessary).

The skeleton of a renderer class is shown bellow.

public static class XEOHTMLRenderer extends XUIRenderer {
            
        @Override
        public void encodeBegin(XUIComponentBase component) throws IOException {}
        
        @Override
        public void encodeEnd(XUIComponentBase component) throws IOException {}

        @Override
        public void decode(XUIComponentBase component) { super.decode(component); }
        
}

There are three main methods in a renderer class. The encodeBegin, encodeEnd and decode methods. The encodeBegin and encodeEnd methods are automatically invoked to render the start of the component and the end of the component (in between, if the component has any child components, their renderers will be invoked). The main idea with these two methods is, in the encodeBegin method to create a div with the component's id and in the encodeEnd method to close that div (after creating or before closing the placeholder you generate the html that will actually how the component is rendered). The decode method allows you to retrieve values sent from the component when the form inside the viewer is submitted to the server.

In order to generate an output you have to write content to the XUIResponseWriter. For example, rendering a placeholder for the component and the string Hello World (in bold) could be done with the following code:

@Override
        public void encodeBegin(XUIComponentBase component) throws IOException {
            
            XUIResponseWriter w = getResponseWriter();
            w.startElement(HTMLTag.DIV , component );
            w.writeAttribute( HTMLAttr.ID , component.getClientId(), null );
            w.write(" <b> Hello World in </b> ");
        }

and

@Override
        public void encodeEnd(XUIComponentBase component) throws IOException {
            
            XUIResponseWriter w = getResponseWriter();
            w.endElement( HTMLTag.DIV );
            
}

Notice that the XUIResponseWriter has methods ( startElement, endElement, writeAttribute) that ease the creation of HTML tags (and auxiliary classes HTMLTag and HTMLAttr with static properties with the names of all HTML tags and attributes so that you don't need to write the tags yourself (although you can, as depicted in the code above)).

_

Adding JavaScript to a component

If you need to add a piece of javascript code when rendering your component you can do it the following way:

XUIScriptContext scriptContext = getRequestContext().getScriptContext();
StringBuilder sCode = new StringBuilder();
//Generate the javascript
...
scriptContext.add(XUIScriptContext.POSITION_FOOTER, "SCRIPT_ID", sCode);

You can add the script to the header or footer of the page. When adding full scripts (aka, making includes) you should add them using POSITION_HEADER so that they are available later on. When adding scripts to be executed, or functions that are invoked by buttons, links or alert messages you should POSITION_FOOTER.

The "SCRIPT_ID" parameter is the identifier of the script. If you want to replace an existing script you should add it with the same id as the previous one.

Using the component as a servlet

It's possible to use the custom component as a servlet. Imagine that you want to create a component that displays a dynamically generated image in a viewer (such as a chart, for example). You can use the renderer class to generate an html img tag and set the source of the image to be the address of the component's servlet.

In order for you to do that, the renderer class must implement the netgest.bo.xwc.framework.XUIRendererServlet interface which declares the following method:

public void service(ServletRequest oRequest, 
   ServletResponse oResponse, XUIComponentBase oComp ) throws IOException;

The service method has access to the HttpRequest and HttpResponse just like an ordinary servlet, as such it can return the bytes representing a generated image. The service method also has access to the component, so that you can check property values and generate the response accordingly.

In order to create the url to call the servlet of a component you can use the methods in the netgest.bo.xwc.components.util.ComponentRenderUtils class which has the following methods:

public static String getCompleteServletURL(XUIRequestContext reqContext, String clientID);
public static String getServletURL(XUIRequestContext reqContext, String clientID);

Each of the previous methods will return a string with the url to invoke the servlet for the component identifier passed as parameter (which, for any given component can reached through the getClientId method)

Registering the component in the project

If you use XEO Studio's wizard to create a XEO Web Component it will automatically be registered for you, but in case you have to make a change to anything after you created the component, here's how:

If the root folder of your project's source code there should be a file named XWVProjectComponents.xml which has the following structure:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xwc:config xmlns:xwc="http://www.netgest.net/xeo/xwc">
    <xwc:renderKits>
        <xwc:renderKit componentNamespace="my" name="XEOHTML">
            <xwc:render for="myComponent">org.example.components.MyComponent$XEOHTMLRenderer</xwc:render>
        </xwc:renderKit>
    </xwc:renderKits>


    <xwc:components namespace="my">
        <xwc:component name="myComponent">
            <xwc:className>org.example.components.MyComponent</xwc:className>
            <xwc:description>Sample custom component</xwc:description>
        </xwc:component>

    </xwc:components>
</xwc:config>

There are two entries for each registered component. One for the renderer class and another for the implementation class. Components are grouped by their namespace (as depicted in the XML declaration above, for the "myComponent" component registered in the "my" namespace).

Using the Component in a Viewer

If you declared (and you should) the component in a custom namespace you need to declare that namespace in the xml definition of a viewer, like in the following example:

<xvw:root 
  xmlns:xvw="http://www.netgest.net/xeo/xvw" 
  xmlns:xeo="http://www.netgest.net/xeo/xeo"
  xmlns:my="http://www.mycompany.com/my" 
>

After that, you use it like any other component. Setting its properties in the xml declaration, if you take the "myComponent" example from above you could use it like the following:

<my:myComponent text='Hello World' />

If the properties were declared as "bindable", you could also use the following (and having a getText method in the backing bean):

<my:myComponent text='#{viewBean.text}' />

_

Creating a custom component (using XEO Studio)

To create a new custom component you can use XEO Studio's wizard. Go to the "File" menu, choose "New" and find "XEO Web Component", the window depicted in figure CustomComp.1 should appear.

Figure CustomComp.1 - Wizard for creating a custom component

In the Java Package field choose an existing package, for the namespace choose "my" and name the component "MyComponent" (description can be empty). XEO Studio will generate a sample component that you can customize.

The generated component will have three properties: text, bold and italic and is designed to output text (with the possibility of the text being bold and/or italic)

You can remove all of the properties and logic from the implementation class as well as remove any code from the renderer class methods.

Topic attachments
I Attachment Action Size Date Who Comment
PNGpng Wizard.png manage 31.2 K 2011-02-18 - 09:21 PedroRio  
Topic revision: r10 - 2013-04-09 - PedroRio
 

No permission to view TWiki.WebTopBar

This site is powered by the TWiki collaboration platform Powered by Perl

No permission to view TWiki.WebBottomBar