Introduction to GWT UI Binders & Templates

As I touched on in a recent post about HTML document flow, I strongly believe that making decent GWT applications depends on understanding the underlying HTML that you are creating. In other words, you can’t get away with making a web application without actually understanding HTML and CSS :) Using GWT is no exception to this rule.

One of the biggest improvements I’ve seen in GWT in this respect is the introduction of UiBinders, also known as “Declarative Layout”.¬†UiBinders provide a way for you to declare the layout of your application and widgets using traditional HTML and CSS rather than programatically in java. This provides you with a much richer way to create your application.

Think of UiBinders as stateful templates. Unlike traditional templates that simply know how to render provided data into static HTML, UiBinder templates describe how a single Widget should be represented in the DOM and provides that widget with references to the created elements. This makes it easier for the widget to interact with the elements, such as setting up events or modifying content without having to make additional DOM lookups.

As an example, lets say you were creating a chat widget, similar to the chat windows found in Gmail or Google+. Since it’s quite tedious to make such a complex UI by hand in Java, you’d want to use a UiBinder template. In such a widget, you’d probably have a send button and some sort of panel that displays your chat log. In this case your widget could maintain references to those two elements in the DOM but leave their rendering and markup up to the template.

Example

Ready for something more concrete? Lets see how this might look in code:

public final class ChatWidget extends Composite {

  // The @UiField annotation tags elements that the widget should
  // maintain references too.
  @UiField Button sendButton;
  @UiField DivElement chatLog;

  @Inject
  ChatWidget() {
    initWidget(binder.createAndBindUi(this));
  }

  @UiHandler("sendButton")
  void handleSendClick(ClickEvent event) {
    // send message...
  }

  public void appendMessage(String message) {
    chatLog.setInnerText(chatLog.getInnerText() + message);
  }

  private static final Binder binder = GWT.create(Binder.class);
  interface Binder extends UiBinder<Widget, ChatWidget> {}
}

And a corresponding ui.xml binder:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">

<ui:UiBinder
   xmlns:gwt="urn:import:com.google.gwt.user.client.ui"
   xmlns:ui="urn:ui:com.google.gwt.uibinder">

  <ui:style>
    .log {
      border: 1px solid black;
      background: #CCC;
    }
   
    .button {
      background: blue;
    }
  </ui:style>
 
  <gwt:HTMLPanel>
    Chat History:
    <div ui:field="chatLog" class="{style.log}"/>
    <gwt:Button ui:field="sendButton" styleName="{style.button}" />
  </gwt:HTMLPanel>

</ui:UiBinder>

 

Breaking the example down

Rendering the template

There is quite a bit going on here – so lets break it down a bit. The first step in making your widget template aware is declare a custom UiBinder class and instantiate it using GWT.create(). This basically creates a class that can render your template on demand.

private static final Binder binder = GWT.create(Binder.class);
interface Binder extends UiBinder<Widget, ChatWidget> {}

The next step is to actually use your template. You can see this in the widget’s constructor where it calls binder.createAndBindUi(this). This effectively tells the binder to render a new instance of the template and bind it to the current widget. It’s here the all the magic is done in terms of providing the widget to the created DOM elements.

Defining the Template

When you define a Binder class, you should create a corresponding WidgetName.ui.xml. This xml file will contain both your html and css.

Your template files commonly have two top level elements:

  • A ui:style tag that contains all of your css definitions for this template. You can reference these style names in this template file by using “{style.class-name}”
  • A root or top level widget for this template. In this example it’s the HTMLPanel. Note that you can only have one top level widget – everything else must go underneath

Tip: An HTMLPanel is a common choice for use as a top level widget, since it allows you to mix raw html and widgets within your template.

Setting up events and updating state

To make your widget aware of elements in your template, you use the special “ui:field” attribute. Any element that has a field attribute can be referred to directly in your java class by defining a @UiField annotated member variable of a corresponding type. You can then have your class change the state, listen to events, change styles, etc – and have your changes updated in the UI immediately.

// In ui.xml file:
<gwt:Button ui:field="myButton" />

// In java file:
@UiField Button myButton;

// Elsewhere in the java file:
myButton.setText("Huzzah!");
myButton.setEnabled(false);

Note that you can refer to both Widgets and underlying DOM elements in your template file. If you want to refer to a DOM element, you’ll need to use the gwt dom classes, such as DivElement or SpanElement

To listen to events, you can use GWT’s shorthand @UiHandler notation (warning: only works for widgets that expose that event). Simply annotate any method (name doesn’t matter) with @UiHandler(“fieldName”) and have it accept the relevant event as a parameter.

 @UiHandler("sendButton")
  void handleSendClick(ClickEvent event) {
    // do something...
  }

The downside with the UiHandler annotation is that it only works with event’s that the widget normally exposes (such as click events on buttons). It doesn’t support any of the regular DOM events, so the second you want to go outside the fairly limited API it doesn’t work.

The common way to listen to any of those DOM events is to use the “addDomHandler” method. Example:

sendButton.addDomHandler(
        new MouseOverHandler() {
          @Override
          public void onMouseOver(MouseOverEvent event) {
            // Do something.
          }
        },
        MouseOverEvent.getType());

 

Further Reading

This should get you started with creating some basic templates. However, I also recommend giving GWT’s UIBinder documentation a quick scan which that has lots of extra details.

This entry was posted in Google Web Toolkit. Bookmark the permalink.

Comments are closed.