8.5. Server-Side Components

Server-side components provide the API for user applications to build their user interface. Many applications do not ever need to bother with the client-side implementation of the standard components, but those that use their own GWT widgets need to have corresponding server-side components.

A server-side component has two basic tasks: it has to be able to serialize its state variables to the corresponding client-side component, and deserialize any user input received from the client. Many of these tasks are taken care of by the component framework.

Component Tag Name

Server-side components are identified with a unique UIDL tag name, which must be returned by the getTag() method. The tag should follow XML rules for element names, that is, only characters a-z, A-Z, 0-9, and _, and not begin with a number. Actually, as IT Mill Toolkit Release 5 uses a JSON notation for serialization, the tag syntax is more relaxed, but we nevertheless recommend using a stricter syntax. UIDL is detailed in Chapter 10, User Interface Definition Language (UIDL) together with lists of reserved tags. The server-side implementation of the Color Picker component defines the tag as follows:

    public String getTag() {
        return "colorpicker";
    }

On the client side, this tag is mapped to a GWT widget. The mapping from server-side to client-side components is actually one to many; a server-side component can manifest as several client-side components, depending on its parameters. For example, a server-side Button can manifest either as an IButton or ICheckBox in client, depending on the switchMode attribute. For the client side, see Section 8.2, “Google Web Toolkit Widgets” above.

The implementation of the server-side component is broken down into server-client serialization and client-server deserialization in the following sections. We will also present the complete example of the server-side implementation of the Color Picker component below.

8.5.1. Server-Client Serialization

The server-side implementation of a component must be able to serialize its data into a UIDL message that is sent to the client. You need to override the paintContent() method, defined in AbstractComponent. You should call the superclass to allow it to paint its data as well.

The data is serialized with the variants of the addAttribute() and addVariable() methods for different basic data types.

The UIDL API offered in PaintTarget is covered in Section 10.1, “API for Painting Components”.

8.5.2. Client-Server Deserialization

The server-side component must be able to receive state changes from the client-side widget. This is done by overriding the changeVariables() method, defined in AbstractComponent. A component should always call the superclass implementation in the beginning to allow it handle its variables.

The variables are given as objects in the variables map, with the same key with which they were serialized on the client-side. The object type is likewise the same as given for the particular variable in updateVariable() in the client-side.

@Override
public void changeVariables(Object source, Map variables) {
    // Let superclass read any common variables.
    super.changeVariables(source, variables);

    // Sets the currently selected color
    if (variables.containsKey("colorname") && !isReadOnly()) {
        final String newValue = (String) variables.get("colorname");
        // Changing the property of the component will
        // trigger a ValueChangeEvent
        setValue(newValue, true);
    }
}

The above example handles variable changes for a field component inheriting AbstractField. Fields have their value as the value property of the object. Setting the value with setValue(), as above, will trigger a ValueChangeEvent, which the user of the component can catch with a ValueChangeListener.

Contained components, such as components inside a layout, are deserialized by referencing them by their paintable identifier or PID.

8.5.3. Extending Standard Components

Extending standard components is one way to develop new components that have some additional features.

Every component needs to have a unique UIDL tag that is used to create and communicate with widgets on the client-side. The tag is normally unique for server-side components. The minimal requirement for the server-side component is that you reimplement the getId() method that provides the tag.

If your extension component contains any specific state variables, you need to handle their serialization in paintContent() and deserialization in changeVariables() and call the superclass implementation in the beginning. See Section 8.5.1, “Server-Client Serialization” Section 8.5.2, “Client-Server Deserialization” above for details.

The client-side implementation goes also much like for regular custom widgets.

8.5.4. Example: Color Picker Server-Side Component

The following example provides the complete server-side ColorPicker component for the Color Picker example. It has only one state variable: the currently selected color, which is stored as the property of the component. Implementation of the Property interface is provided in the AbstractField superclass of the component. The UIDL tag name for the component is colorpicker and the state is communicated through the colorname variable.

package com.itmill.toolkit.demo.colorpicker;

import java.util.Map;
import com.itmill.toolkit.terminal.PaintException;
import com.itmill.toolkit.terminal.PaintTarget;
import com.itmill.toolkit.ui.*;

public class ColorPicker extends AbstractField {
    
    public ColorPicker() {
        super();
        setValue(new String("white"));
    }

    /** The property value of the field is an Integer. */
    public Class getType() {
        return String.class;
    }

    /** Tag is the UIDL element name for client-server communications. */
    public String getTag() {
        return "colorpicker";
    }
    
    /** Set the currently selected color. */
    public void setColor(String newcolor) {
        // Sets the color name as the property of the component.
        // Setting the property will automatically cause repainting of
        // the component with paintContent().
        setValue(newcolor);
    }
    
    /** Retrieve the currently selected color. */
    public String getColor() {
        return (String) getValue();
    }

    /** Paint (serialize) the component for the client. */
    public void paintContent(PaintTarget target) throws PaintException {
        // Superclass writes any common attributes in the paint target.
        super.paintContent(target);
        
        // Add the currently selected color as a variable in the paint target.
        target.addVariable(this, "colorname", getColor());
    }
    
    /** Deserialize changes received from client. */
    public void changeVariables(Object source, Map variables) {
        // Sets the currently selected color
        if (variables.containsKey("colorname") && !isReadOnly()) {
            String newValue = (String) variables.get("colorname");
            
            // Changing the property of the component will
            // trigger a ValueChangeEvent
            setValue(newValue,true);
        }
    }
}