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:
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.
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.
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:
updateComponent()
and return if it
succeeds
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.
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
Type | Description | UIDL 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.
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); }
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); } }