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.
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.
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.