8.3. Integrating a GWT Widget

Integration of GWT widgets with IT Mill Toolkit can be done in two basic ways: by modifying the original class or by inheriting it and adding the integration code in the subclass. The latter way is actually the way the standard client-side components in IT Mill Toolkit are done: they simply inherit the corresponding standard GWT widgets. For example, IButton inherits GWT Button.

The integration code has the following tasks:

  • Manage CSS style class
  • Receive component state from server
  • Send state changes caused by user interaction to server

The integration is broken down in the following sections into server-client deserialization done in updateFromUIDL() and client-server serialization done with updateVariable(). The complete example of the integration of the Color Picker widget is given at the end of this section.

Naming Conventions

While the use of IT Mill Toolkit does not require the use of any particular naming conventions for GWT widgets, some notes regarding naming may be necessary. While Java name spaces make it possible to use identical class names in the same context, it may be useful to try to make them more distinctive to avoid any inconvenience. GWT uses simple names for its standard widgets, such as Button. The standard components of IT Mill Toolkit use identical or similar names, but that does not cause any inconvenience, because the GWT widgets and server-side components of IT Mill Toolkit are never used in the same context. For the client-side components of IT Mill Toolkit, we use the "I" prefix, for example IButton. In the Color Picker example, we use GwtColorPicker for the GWT widget and IColorPicker for the integration implementation. You may wish to follow similar conventions.

8.3.1. Deserialization of Component State from Server

To receive data from the server, a widget must implement the Paintable interface and its updateFromUIDL() method. The idea is that the method "paints" the user interface description by manipulating the HTML tree on the browser. Typically, when using composite GWT components, most of the DOM tree manipulation is done by standard GWT widgets.

An implementation of the updateFromUIDL() method must include some routine tasks:

  • Call updateComponent() and return if it succeeds
  • Manage the component identifier
  • Manage a reference to the ApplicationConnection object. The widget needs to know it to be able to initiate a server request when a browser event occurs.

The latter two of these tasks are not needed if the widget does not handle any user input that needs to be sent to server.

The following excerpt provides a skeleton for the updateFromUIDL() method and shows how the component identifier and connection object reference are managed by a widget.

    String uidlId;
    ApplicationConnection client;

    ...

    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
        if (client.updateComponent(this, uidl, true))
            return;
 
        this.client = client;
        uidlId = uidl.getId();

        ...
    }

The updateComponent() call has several functions important for different kinds of components. It updates various default attributes, such as disabled, readonly, invisible, and (CSS) style attributes. If the manageCaption argument is true, the call will also update the caption of the component. By default, the caption is managed by the parent layout of the component. Components, such as a Button, that manage the caption themselves, do not need management of the caption.

The updateComponent() is also part of the transmutation mechanism that allows a single server-side component to have alternative client-side implementations, based on its parameters. For example, the Button server-side component can manifest either as a clickable IButton or as a switchable ICheckBox widget on the client-side. If the parameters are changed, the client-side widget can be replaced with another dynamically. Determination of the correct implementation is done in a WidgetSet. If updateComponent() returns true, the client-side engine can attempt to replace the implementation. For more details on the transmutation mechanism, see Section 8.4, “Defining a Widget Set”.

The component identifier is used when the component needs to serialize its updated state to server. The reference to the application connection manager is needed to make the server request. If a component does not have any state changes that need to be sent to the server, management of the variables is not needed. See Section 8.3.2, “Serialization of Component State to Server” below for further details.

The design of the client-side framework of IT Mill Toolkit, because the Paintable is an interface and can not store any references. Having an API layer between GWT and custom widgets would be a much more complicated solution.

8.3.2. Serialization of Component State to Server

User input is handled in GWT widgets with events.

User input is passed to the server using the updateVariable() method. If the immediate parameter is false, the value is simply added to a queue to be sent to the server at next AJAX request. If the argument is true, the AJAX request is made immediately, and will include all queued updates to variables.

if (uidl_id == null || client == null)
        return;

client.updateVariable(uidl_id, "myvariable", newvalue, immediate);

The client of the above example is a reference to the ApplicationConnection object that manages server requests. The uidl_id argument is the UIDL identifier obtained during a updateFromUIDL() call with uidl.getId() method.

The updateVariable() method has several varieties to send variables of different types.

Table 8.1. UIDL Variable Types

TypeDescriptionUIDL Type
String String object. s
int Native integer value. i
long Native long integer value. l
float Native single-precision floating-point value. f
double Native double-precision floating-point value. d
boolean Native boolean value. b
Object[] Array of object data. The toString() method is used to serialize each of the objects. The content strings are escaped with escapeString(), to allow characters such as quotes. a

The immediate argument is described below.

This serialization mechanism is intended to be as simple as possible in most cases, when the user input is typically just one state variable, while also allowing the serialization of more complex data, if necessary.

Immediateness

Server-side components that inherit AbstractComponent have an immediate attribute, set with setImmediate(). This attribute dictates whether a component makes a server request immediately when its state changes, or only afterwards. For example, there is no need to send the contents of a "Username" TextField before the "Login" button has been clicked. On the other hand, the server can set the TextField as immediate to receive changes for example when the component loses focus.

Most widgets should support immediateness by receiving the immediate attribute from the UIDL message that renders the widget. The following example is extracted from the ITextField implementation.

    // Store the immediate attribute in a member variable
    private boolean immediate = false;
    ...

    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
        if(client.updateComponent(this, uidl, true))
            return;

        // Receive and store the immediate attribute
        immediate  = uidl.getBooleanAttribute("immediate");
        ...
    }

    public void onChange(Widget sender) {
        if(client != null && id != null) {
            // Use the stored immediate attribute to say whether or not
            // make the server request immediately.
            client.updateVariable(id, "text", getText() , immediate);
        }
    }

In some widgets, the immediate attribute would have little meaning, and in fact an accidental false value would cause undesired behaviour. For example, a button is always expected to send a request to the server when it is clicked. Such widgets can simply use true for the immediate argument in updateVariable(). For example, IButton does as follows:

public void onClick(Widget sender) {
    if (id == null || client == null)
        return;
    client.updateVariable(id, "state", true, /* always immediate */ true);
}

8.3.3. Example: Integrating the Color Picker Widget

Below is a complete example of an integration component for the Color Picker example. It demonstrates all the basic tasks needed for the integration of a GWT widget with its server-side counterpart component.

import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
import com.itmill.toolkit.terminal.gwt.client.Paintable;
import com.itmill.toolkit.terminal.gwt.client.UIDL;

public class IColorPicker extends GwtColorPicker implements Paintable {

    /** Set the CSS class name to allow styling. */
    public static final String CLASSNAME = "example-colorpicker";

    /** Component identifier in UIDL communications. */
    String uidlId;

    /** Reference to the server connection object. */
    ApplicationConnection client;

    /**
     * The constructor should first call super() to initialize the component and
     * then handle any initialization relevant to IT Mill Toolkit.
     */
    public IColorPicker() {
        // The superclass has a lot of relevant initialization
        super();

        // This method call of the Paintable interface sets the component
        // style name in DOM tree
        setStyleName(CLASSNAME);
    }

    /**
     * This method must be implemented to update the client-side component from
     * UIDL data received from server.
     * 
     * This method is called when the page is loaded for the first time, and
     * every time UI changes in the component are received from the server.
     */
    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
        // This call should be made first. Ensure correct implementation,
        // and let the containing layout manage caption, etc.
        if (client.updateComponent(this, uidl, true))
            return;

        // Save reference to server connection object to be able to send
        // user interaction later
        this.client = client;

        // Save the UIDL identifier for the component
        uidlId = uidl.getId();

        // Get value received from server and actualize it in the GWT component
        setColor(uidl.getStringVariable("colorname"));
    }

    /** Override the method to communicate the new value to server. */
    public void setColor(String newcolor) {
        // Ignore if no change
        if (newcolor.equals(currentcolor.getText()))
            return;

        // Let the original implementation to do whatever it needs to do
        super.setColor(newcolor);

        // Updating the state to the server can not be done before
        // the server connection is known, i.e., before updateFromUIDL()
        // has been called.
        if (uidlId == null || client == null)
            return;

        // Communicate the user interaction parameters to server. This call will
        // initiate an AJAX request to the server.
        client.updateVariable(uidlId, "colorname", newcolor, true);
    }
}