GWT UiFactory


December 5, 2013 Maithilish

6.4. UiFactory

UiBinder constructs a widget in multiple ways, and they fall into two categories.
  • UiBinder creates the widget and insert it to UI structure.
  • Application code creates the widget and hand it over to UiBinder to insert it to UI structure.
Widget creation delegated to UiBinder

This is of two types – without value attributes and with value attributes.

Without value attributes – For <f:TitlePanel ui:field=”titlePanel” />, UiBinder uses default constructor of TitlePanel to create the widget.
With value attributes and @UiConstructor – For <f:TitlePanel ui:field=”titlePanel” myText=”some text” />, UiBinder looks for a constructor annotated with @UiConstructor and if there is one with matching parameters then uses it to create the widget.

        @UiConstructor
        public TitlePanel(String myText){
                initWidget(binder.createAndBindUi(this));
                label.setText(myText);
        }

With value attributes and setter methods – In case there is no constructor that fulfills the above conditions, then UiBinder requires a setter method to set the value of the attribute. In the previous example, UiBinder looks for method setMyText(). It is important to note that UiBinder calls the setter once widget is instantiated.

        public TitlePanel(){
                initWidget(binder.createAndBindUi(this));
        }

        public void setMyText(String myText) {
                label.text = myText;
        }

Create Widget and handover it to UiBinder

In this method, we have to create the widget and handover it to UiBinder which adds the widget to UI structure as defined in ui.xml, and there are two ways to do this.

Field marked with @UiField(provided=true) – In case field is marked with @UiField(provided=true) then we have to instantiate the widget before calling UiBinder.createAndBindUi() method.

        @UiField(provided = true)
        Image image;

        public SomeWidget() {

                // create image and then call createAndBind

                image = new Image(imageResource);        
                initWidget(binder.createAndBindUi(this));
        }

GWT UiFactory

In case you wish to bypass the above mentioned constructors i.e. default constructor or UiConstructor or constructor plus setter methods, then use @UiFactory.

        @UiFactory
        public TitlePanel titlePanelFactory(String myText){
                TitlePanel tp = new TitlePanel();

                // any other code

                return tp;
        }

This factory method has to be added to java file where TitlePanel is used and not in TitlePanel.java. Once factory method is defined then we have to, instantiate and return proper widget, and Uibinder adds the widget returned by the factory method to UI Tree. We are free to add any other logic within in factory method.
GWT UiFactory is suitable when we want to handle multiple instances of a widget in a uniform manner. Let’s imagine, we have three instances of Zwidget then using UiField to attach a handler results in following code

        @UiField
        ZWidget  w1,  w2, w3;

        EventBus.get().addHandler(SomeEvent.TYPE, w1);
        EventBus.get().addHandler(SomeEvent.TYPE, w2);
        EventBus.get().addHandler(SomeEvent.TYPE, w3);

UiFactory compacts the code as

        @UiFactory
        public ZWidget zFactory(){
                ZWidget w = new ZWidget();
                EventBus.get().addHandler(SomeEvent.TYPE, zw);
                return w;
        }

Before creating a widget UiBinder checks whether there is a factory method that returns the widget and if there is one, it uses that method to obtain the widget instead creating one. It is our job to create and return the widget in the factory method, and we may add any other logic inside the factory method.

With that brief introduction to GWT UiFactory we now move to create AutoSuggest and TitlePanel with UiFactory. First remove the handler added in the constructor of Snapshot and add factories.
in.fins.client.content/Snapshot.java

        @UiFactory
        public AutoSuggest autoSuggestFactory() {
                AutoSuggest autoSuggest = new AutoSuggest();
                return autoSuggest;
        }

        @UiFactory
        public TitlePanel titlePanelFactory() {
                TitlePanel tp = new TitlePanel();
                EventBus.get().addHandler(NameEvent.TYPE, tp);
                return tp;
        }

UiFactory for AutoSuggest and TitlePanel is unnecessary, but soon we are going have widgets with dozens of instances and as explained earlier using UiFactory for such widgets makes a lot of sense. For the sake of uniformity and also to make readers comfortable with @UiFactory we have used them even, for widgets with single instances like AutoSuggest and TitlePanel.
Event wiring
Figure 6.2. Event wiring


Access the application and test event by selecting a symbol name in suggestion box and on TitlePanel displays the name, and it seems things are working as expected. Test it further by selecting Symbol menu to open a second tab and then select a name and this causes TitlePanel in other tabs to display selected name. NameEvent fired by AutoSelection.handleSelection() propagates also to TitlePanel in other tabs.
This is bound happen as we told the EventBus that TitlePanel is interested in NameEvent. EventBus dispatches the NameEvent to each and every TitlePanel and thats why event fired by Tab 2 updates Tab2-TitlePanel as well as in Tab1-TitlePanel.
Event broadcast
Figure 6.3. Event broadcast


Event generated by widgets in one tab should be dispatched to interested widgets in that tab only, and events should not propagate to other tabs.
EventBus API provides two methods exactly for this.
  • fireEventFromSource(event, source)
  • addHandlerToSource(type, source, handler)
These methods tag event to a source and listens only for events which are tagged to a specific source.
in.fins.client.content/Snapshot.java


        @UiFactory
        public AutoSuggest autoSuggestFactory() {
                AutoSuggest autoSuggest = new AutoSuggest();
                autoSuggest.setEventSource(this);
                return autoSuggest;
        }

        @UiFactory
        public TitlePanel titlePanelFactory() {
                TitlePanel tp = new TitlePanel();
                EventBus.get().addHandlerToSource(NameEvent.TYPE, this, tp);
                return tp;
        }

in.fins.client.widget/AutoSuggest.java

        private Object eventSource;

....


        @UiHandler("suggestBox")
        public void handleSelection(SelectionEvent<SuggestOracle.Suggestion> s) {
                log.fine("Selection : " + suggestBox.getText());
                EventBus.get().fireEventFromSource(new NameEvent(suggestBox.getText()),
                                eventSource);
                suggestBox.setText("");
        }

        public void setEventSource(Object eventSource) {
                this.eventSource = eventSource;
        }

Snapshot is used as eventSource because it is visible to both AutoSuggest and TitlePanel. Now AutoSuggest fires NameEvent tagged to a Snapshot instance, and TitlePanel handles NameEvent tagged with that Snapshot instance and with this Snapshot tab is no longer interested in NameEvent originated in other tabs.

Constructor or UiFactory

Less widget instances or instance behavior is heterogeneous go for constructor.
Multiple widget instances and instances have similar behavior then go for UiFactory.
Next section designs FactPanel to display some facts about the Symbol selected by the user.