My favorites | Sign in
Google
                
Search
for
Updated Mar 09, 2009 by rjrjr@google.com
Labels: Type-Library
UiBinder  
UI Binder Use Cases

GWT UI Binder Use Cases

Ray Ryan

This document provides various use cases for the use of the UiBinder, a proposed service to generate Widget and DOM structures from XML markup.

Background

There are problems with the declarative ui template service as it was originally proposed

In addressing these issues, we have talked about encouraging a proxy style of use (basically, use Composite to wrap whatever widget gets GWT.create()'d), but dislike the extra object creation implied. We also hope for a system that can choose to use innerHTML, cloning, or DOM assembly as makes sense per browser type. These shortcomings could be addressed by a combination of developer discipline (yuck) and perhaps the builder pattern, but we still found ourselves faced with the likelihood of hurried developers wrapping an unneeded, generated object.

Emily hit upon the idea of the Configurator (here rechristened UiBinder). It’s like a factory, but responsible for filling in the fields of a Widget (or other object) that someone else instantiates, rather than instantiating one itself. This seems to offer all the benefits of a builder, with no concerns of extra object creation, and as a nice side effect avoids a lot of boilerplate. This document illustrates its application in various use cases.

/**
 * Interface implemented by classes that generate DOM or Widget structures from
 * ui.xml template files, and which inject portions of the generated UI into the
 * fields of an owner.
 * <p>
 * The generated UiBinder implementation will be based on an xml file resource
 * in the same package as the owner class, with the same name and a "ui.xml"
 * suffix. For example, a UI owned by class {@code bar.baz.Foo} will be sought
 * in {@code /bar/baz/Foo.ui.xml}. (To use a different template file, put the
 * {@link UiTemplate} annotation on your UiBinder interface declaration to point
 * the code generator at it.)
 *
 * @param <U> The type of the root object of the generated UI, typically a
 *          subclass of {@link com.google.gwt.dom.client.Element} or
 *          {@link com.google.gwt.user.client.ui.UIObject}
 * @param <O> The type of the object that will own the generated UI
 */
public interface UiBinder<U, O> {
  
  /**
   * Creates and returns the root object of the UI. If possible the creation of
   * the rest of the UI is deferred until {@link #bindUi} is called.
   */
  U createUiRoot();

  /**
   * Completes the creation of a UI started with a call to {@link #createUiRoot},
   * and fills any owner fields tagged with {@link UiField}.
   */
  void bindUi(U root, O owner);
  
  /**
   * Convenience method that creates and binds the UI in one call. May be
   * optimized rather than actually calling both {@link #createUiRoot} and
   * {@link #bindUi}.
   */
  U createAndBindUi(O owner);
}

Hello World

Make a simple generated UI, with a named element, and without widgets.

<!-- HelloWorld.ui.xml -->

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
  <div>
    Hello, <span ui:field='nameSpan'/>.
  </div>
</ui:UiBinder>
public class HelloWorld extends UIObject { // Could extend Widget instead

  interface MyUiBinder extends UiBinder<DivElement, HelloWorld> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField SpanElement nameSpan;

  public HelloWorld(String name) {
    // call to createAndBindUi sets this.nameSpan
    setElement(uiBinder.createAndBindUi(this));
    nameSpan.setInnerText(name);
  }
}

// Use:

SpanElement helloWorld = new HelloWorld("World").getElement();

Hello Composite World

Make a simple widget-based UI

<!-- HelloWidgetWorld.ui.xml -->

<ui:UiBinder 
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'>
  <g:HTMLPanel>
    Hello, <g:ListBox ui:field='listBox'/>.
  </g:HTMLPanel>
</ui:UiBinder>
public class HelloWidgetWorld extends Composite {

  interface MyUiBinder extends UiBinder<Widget, HelloWidgetWorld> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField ListBox listBox;

  public HelloWidgetWorld(String... names) {
    // sets listBox
    initWidget(uiBinder.createAndBindUi(this));
    for (String name : names) { listBox.addItem(name); }
  }
}

// Use:

HelloWidgetWorld helloWorld =
  new HelloWidgetWorld("able", "baker", "charlie");

Hello Deferred World

Note that this example uses the same ui.xml file as the first. Templates need have no knowledge of what kind of binder will use them.

<!-- HelloWorld.ui.xml -->

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
  <div>
    Hello, <span ui:field='nameSpan'/>.
  </div>
</ui:UiBinder>
public class Deferral extends Widget {

  interface MyUiBinder extends UiBinder<SpanElement, Topical> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField SpanElement nameSpan;

  public Deferral(String name) {
    setElement(uiBinder.createUiRoot());
  }

  @Override protected onAttach() {
    // set nameSpan
    uiBinder.bindUi((SpanElement)getElement(), this);
    nameSpan.setInnerText(name);
    super.onAttach();
  }
}

// Use:

Deferral helloDeferral = new Deferral("World");

Putting a label on a checkbox (referring to generated ids within a template)

You want to make your personal variant on the single most common widget, a checkbox with a nice, accessible HTML label element tied to it:

<!-- LabeledCheckBox.ui.xml -->

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
  <span>
    <input type='checkbox' ui:field='myCheckBox'>
    <label ui:for='myCheckBox' ui:field='myLabel'/>
  </span>
</ui>
public class LabeledCheckBox extends Widget {
  interface MyUiBinder extends UiBinder<Widget, LabeledCheckbox> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField InputElement myCheckBox;
  @UiField LabelElement myLabel;

  public LabeledCheckBox() { setElement(uiBinder.createAndBindUi(this)); }
 
  public void setValue(boolean b) { myCheckBox.setChecked(b); }

  public boolean getValue() { return myCheckBox.isChecked(); }

  public void setName(String name) { myLabel.setInnerText(name); }

  public String getName() { return myLabel.getInnerText(); }
}

The proposal here is that a ui: prefix on any attribute other than id fills it with the id generated for a corresponding ui:field.

There are type matching issues here. The ui:field of a DOM element is a string id, while that for a UIObject is typed. So, this should fail with a type mismatch:

<some:WidgetOfSomeKind ui:field='theWidget'> <label g:for='theWidget' />

The use of attribute prefixing for this would be a mistake, a bad use of XML. In particular, it fights the use of XML tools to enforce things like, "all labels must have a 'for' attribute."

Instead, we should bite the bullet and adopt a mini-expression language, something like:
<ui:UiBinder
  xmlns:ui='urn:ui:com.google.gwt.uibinder'>
  <span>
    <input type='checkbox' ui:field='myCheckBox'>
    <label for='{myCheckBox}' ui:field='myLabel'/>
  </span>
</ui:UiBinder>

Using an ImmutableResourceBundle (e.g. CssResource) with a UiBinder

<!-- LogoNamePanel.ui.xml -->
<ui:UiBinder
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'
  xmlns:res='urn:with:com.my.app.widgets.logoname.Resources'>
  <g:HTMLPanel>

    <img res:apply='logoImage'>

    <div res:class='style.mainBlock'>
      <div res:apply='style.userPictureSprite'>
        Well hello there
        <span res:class='style.nameSpan' ui:field='userNameField'/>
      </div>
    </div>
  </g:HTMLPanel>
</ui:UiBinder>
public class LogoNamePanel extends Composite {
  interface MyUiBinder extend UiBinder<Widget, LogoNamePanel> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField SpanElement nameSpan;

  public LogoNamePanel() {
    initWidget(uiBinder.createAndBindUi(this));
  }

  public void setUserName(String userName) {
    nameSpan.setInnerText(userName);
  }
}

public interface Resources extends ClientBundle {
  @Resource("Style.css")
  Style style();

  @Resource("Logo.jpg")
  ImageResource widgetyImage();

  public interface Style extends CssResource {
    String mainBlock();
    String nameSpan();
    Sprite userPictureSprite();
  }
}

The with: uri type marks an object whose methods can be called to fill in attribute values. If no public api is provided to set the "with" argument (as in this example), it must be instantiable by GWT.create().

An element can be passed as an argument to a method on such resource class via an apply attribute, as illustrated above with the Sprite and ImageResource uses.

Note that there is no requirement that a with: class implement the ClientBundle interface.

As above, this is an abuse of attribute prefixing, and a bad idea. Here again we should use our little expression language:

<ui:UiBinder
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:importcom.google.gwt.user.client.ui'
  xmlns:res='urn:with:com.my.app.widgets.logoname.Resources'>

  <g:HTMLPanel>

    <div class='{res.style.mainBlock}'>
      Well hello there
      <span class='{res.style.nameSpan}' ui:field='userNameField'/>
    </div>
  </g:HTMLPanel>
</ui:UiBinder>

Share ImmutableResourceBundle instances

Extends LogoNamePanel (from the example above) to allow its bundle to be passed in.

public class LogoNamePanel extends Composite {
  interface MyUiBinder extend UiBinder<Widget, LogoNamePanel> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField SpanElement nameSpan;
  final Resources resources;

  public LogoNamePanel(Resources resources) {
    initWidget(uiBinder.createAndBindUi(this));
    this.resources = resources;
  }

  public void setUserName(String userName) {
    nameSpan.setInnerText(userName);
  }

  @UiFactory
  public Resources getResources() {
    return resources;
  }
}

This can be even more concise:

public class LogoNamePanel extends Composite {
  interface MyUiBinder extend UiBinder<Widget, LogoNamePanel> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField SpanElement nameSpan;
  @UiField(provided = true)
  final Resources resources;

  public LogoNamePanel(Resources resources) {
    initWidget(uiBinder.createAndBindUi(this));
    this.resources = resources;
  }

  public void setUserName(String userName) {
    nameSpan.setInnerText(userName);
  }
}

Using a widget that requires constructor args

You have an existing widget that needs constructor arguments.

public CricketScores(String... teamNames) {...} 

You use it in a template.

<!-- UserDashboard.ui.xml -->
< ui:UiBinder
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'
  xmlns:my='urn:import:com.my.app.widgets' >

  <g:HTMLPanel>
    <my:WeatherReport ui:field='weather'/>
    <my:Stocks ui:field='stocks'/>
    <my:CricketScores ui:field='scores' />
  </g:HTMLPanel>
</ui:UiBinder>
public class UserDashboard extends Composite {
  interface MyUiBinder extends UiBinder<Widget, UserDashboard> {}
  private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  public UserDashboard() {
    initWidget(uiBinder.createAndBindUi(this));
  }
}

An error results:

UserDashboard.ui.xml:7:2 [ERROR] com.my.app.widgets.CricketScores
has no default (zero args) constructor. You can define a
@UiFactory annotated method on UserDashboard to create an instance;
mark a CrickectScores field of UserDashboard with @UiField(provided=true)
and put an instance there; or annotate a constructor of CricketScores with 
@UiConstructor to allow its arguments to be provided by the template.

So you either make the @UiFactory method:

public class UserDashboard extends Composite {
  interface MyUiBinder extends UiBinder<Widget, UserDashboard>;
  private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  private final String[] teamNames;

  public UserDashboard(String... teamNames) {
    this.teamNames = teamNames;
    initWidget(uiBinder.createAndBindUi(this));
  }

  /** Used by MyUiBinder to instantiate CricketScores */
  @UiFactory CricketScores makeCricketScores() { // method name is insignificant
    return new CricketScores(teamNames);
  }
}

or perhaps:

public class UserDashboard extends Composite {
  interface MyUiBinder extends UiBinder<Widget, UserDashboard>;
  private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  public UserDashboard() {
    this.teamNames = teamNames;
    initWidget(uiBinder.createAndBindUi(this));
  }

  /** 
   * Used by MyUiBinder to instantiate CricketScores. 
   * Arguments to be filled in the template
   */
  @UiFactory CricketScores(String... teamNames) {
    return new CricketScores(teamNames);
  }
}
<!-- UserDashboard.ui.xml -->
<g:HTMLPanel xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'
  xmlns:my='urn:import:com.my.app.widgets' >

  <my:WeatherReport ui:field='weather'/>
  <my:Stocks ui:field='stocks'/>
  <my:CricketScores ui:field='scores' teamNames='AUS, SAF, WA, QLD, VIC'/>
</g:HTMLPanel>

or annotate the constructor:

public @UiConstructor CricketScores(String... teamNames) {...} 
<!-- UserDashboard.ui.xml -->
<g:HTMLPanel xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'
  xmlns:my='urn:import:com.my.app.widgets' >

  <my:WeatherReport ui:field='weather'/>
  <my:Stocks ui:field='stocks'/>
  <my:CricketScores ui:field='scores' teamNames='AUS, SAF, WA, QLD, VIC'/>
</g:HTMLPanel>

or fill in a field marked with @UiField(provided=true):

public class UserDashboard extends Composite {
  interface MyUiBinder extends UiBinder<Widget, UserDashboard>;
  private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

  @UiField(provided=true)
  final CricketScores cricketScores; // cannot be private

  public UserDashboard(CricketScores cricketScores) {
    // DI fans take note!
    this.cricketScores = cricketScores;
    initWidget(uiBinder.createAndBindUi(this));
  }
}

Apply different xml templates to the same widget

You're an MVC developer. You have a nice view interface, and a templated Widget that implements it. How might you use several different xml templates for the same view?

public class FooPickerController {
  public interface Display {
    HasText getTitleField();
    SourcesChangeEvents getPickerSelect();
  }

  public void setDisplay(FooPickerDisplay display) { ... }
}

public class FooPickerDisplay extends Composite
    implements FooPickerController.Display {
 
  @UiTemplate("RedFooPicker.ui.xml")
  interface RedBinder extends UiBinder<Widget, FooPickerDisplay> {}
  private static RedBinder redBinder = GWT.create(MyUiBinder.class);

  @UiTemplate("BlueFooPicker.ui.xml")
  interface BlueBinder extends UiBinder<Widget, FooPickerDisplay> {}
  private static BlueBinder blueBinder = GWT.create(MyUiBinder.class);

  @UiField HasText titleField;
  @UiField SourcesChangeEvents pickerSelect;

  public HasText getTitleField() {
    return titleField;
  }
  public SourcesChangeEvents getPickerSelect() {
    return pickerSelect;
  }

  protected FooPickerDisplay(UiBinder<Widget, FooPickerDisplay> binder) {
    initWidget(uiBinder.createAndBindUi(this));
  }

  public static FooPickerDisplay createRedPicker() {
    return new FooPickerDisplay(redBinder);
  }

  public static FooPickerDisplay createBluePicker() {
    return new FooPickerDisplay(blueBinder);
  }
}

Comment by dan.baternik, Jul 30, 2008

A declarative ui definitely appears to be step in the right direction. I'm following this very closely :)

Comment by svramu, Aug 15, 2008

I don't know how to link to a comment in another wiki page... Please see my comment/request in http://code.google.com/p/google-web-toolkit-incubator/wiki/CssResource

Comment by svramu, Sep 02, 2008

Thanks to the showcase example, and http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/860aefef0daa2539/8839d9da2f7b28a7 (also http://www.zenika.com/blog/wp-content/uploads/2007/08/tutorial-binding-en.pdf), I managed to have my own generator to bind the HTML to GWT in compile time.

The use case I'm trying is to provide different GUI for same functionality, and with generators I can map the right GUI as one of the compiler permutation. I peeked and used the Locale like JS usage for my GWT module. All neat and clean! The issue I see is that it pastes the whole HTML as it is in the generated html files even in obfuscated mode. Makes sense as any optimization on HTML can be invasive. So I'm looking if the UiBinder will optimize it much more.

Will UiBindier? allow me to create more than one GUI for the same screen (a collection of widgets with logic)? BTW, I use the HTML only for layout (so a designer can supply it). All the components are embedded into it with the logic. Any day I'll be happy to ditch my homemade generator (however sweet it is) for GWT supported one.

Comment by svramu, Sep 02, 2008

oh FooPickerDisplay? says it all! (and that static call ensures only one thing is really instantiated) Sorry should have read more carefully. And the whole model is not alien for me, and this is neat. I thought my model is convoluted with inner interfaces etc, but looks like it is necessary.

Comment by svramu, Nov 10, 2008

http://tattvum.blogspot.com/2008/11/on-gwt-gui-and-pure-pojo-applications.html

" Maybe think this way, take POJO, apply JPA annotation to it, and now add new GWT GUI (ideally generic standard) annotations to it. Create HTML and run GWT over it. Presto. RPC, html binding all taken care of.

GWT can keep adding to the standard data type mapping, and allow custom routes for experts.

In this light declarative UI looks bit out dated (?!) because maybe component binding will become a less common daily chore. "

Comment by chettich, Dec 30, 2008

Nice idea! Looks like XAML.

Comment by dobesv, Jan 08, 2009

If you're looking for a sample implementation of this concept check out my Kiyaa! project where I have already created a declarative UI system based on facelets. Basically you define tag libraries to map tags to UI classes, and you write XHTML templates which contain the widget trees you want to display. Feel free to contact me about this ...

http://code.google.com/p/kiyaa/

Comment by codeoncoffee, May 18, 2009

If you're looking for declaritive markup and code bindings you may be interested in Shandor Xul.

We've implemented a GWT runtime for our Xul framework that supports bindings, overlays, includes, i18n. Plus the same app will run as Swing or SWT. It's been released to the community as Shandor Xul:

http://bitbucket.org/codeoncoffee/shandor-xul/wiki/Home

Comment by andres.a.testi, Jun 07, 2009

I think the extension .xhtml has more sense than .ui.xml

Comment by cristiano.costantini, Jun 09, 2009

Declarative UI is the only thing missing to GWT, and it is the only reason I prefer Flex and WPF, I'm following the evolution of UIBinder very closely.

Comment by cristiano.costantini, Jun 09, 2009

dear andreas.a.test, I think an XHTML file need to have <html>,<head> and <body> tags to be compliant.

Do it is possible to try out this UiBinder? (I'm failing in searching out instructions... it seems it is not present in the 1.6 incubator jar...)

Comment by cwestbrook20, Jun 09, 2009

This was mentioned within a couple of Google I/O 2009. They mentioned that this was used in both Google AdWords? and Google Wave...

Is UI Binder something that will be released soon as open source?

Comment by cristiano.costantini, Jun 15, 2009

I would like to propose a use case: to use XHTML to define GWT widget/panels inside an XHTML host page. Here an example of what I mean:

---

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
		"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xml:lang="it" dir="ltr" 
		xmlns="http://www.w3.org/1999/xhtml" 
		xmlns:ui='urn:ui:com.google.gwt.uibinder'
		xmlns:gwt='urn:import:com.google.gwt.user.client.ui'>
   
	<head>
		<title>Use case for GWT + UiBinder + XHTML</title>
		<script type="text/javascript" src="mymodule/mymodule.nocache.js"></script>
	</head>
	
	<body>
		<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1'
			style="position: absolute; width: 0; height: 0; border: 0"></iframe>
	
		<div id="sample_content_1">
			<!-- same example of the hello world -->
			<ui:UiBinder>
				<div>
					Hello, <span ui:field='nameSpan'/>.
				</div>
			</ui:UiBinder>
		</div>
		
		<div id="sample_content_2">
			<!-- 
				creating a declarative layout for an app, intended to produce a layout
				similar to http://gwt.google.com/samples/Showcase/Showcase.html#CwVerticalPanel 
			-->
			<ui:UiBinder>
				<gwt:VerticalPanel spacing="5" debugId="cwVerticalPanel">
					<gwt:Button text="Button 1" />
					<gwt:Button text="Button 2" />
					<gwt:Button text="Button 3" />
					<gwt:Button text="Button 4" />
					<gwt:Button text="Button 5" />
					<gwt:Button text="Button 6" />
					<gwt:Button text="Button 7" />
					<gwt:Button text="Button 8" />
					<gwt:Button text="Button 9" />		  		
				</gwt:VerticalPanel>
			</ui:UiBinder>
		<div>
	</body>
</html>

---

the idea is that inside the application entrypoint all the <ui:UiBinder> elements are replaced with corresponding GWT widgets.

It seem to me powerful because I can have a server side technology (JSP, PHP etc...) to output my XHTML host page, and have GWT to dynamically build the layout on the needs.

For example it may be useful when you have an application with different user privileges, on which you may have a servlet to handle user log in a Web 1.0 approach, and a JSP to output the correct layout for the priviledges granted to the user (i.e. an administration panel only for Administrators, or no gwt application at all in case of incorrect login....)

Hope this is interesting and that this will be read by the right GWT developers ;-)


Sign in to add a comment