4.3. GWT EventBus and Events
ContentPanel
has to listen for MenuItem
clicks and add tab with
appropriate content.
Traditional Event Handling
Implement ClickHandler in ContentPanel with the following modifications.
in.fins.client.widget/ContentPanel.java
....
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.datepicker.client.DateBox;
.....
public class ContentPanel extends ResizeComposite implements ClickHandler
....
@Override
public void onClick(ClickEvent event) {
String text = ((Anchor) event.getSource()).getText();
addTab(text, new DateBox());
}
....
DateBox
is a placeholder, and actual content replaces it later.
In MenuBar class add a method addClickHandler()
which sets click
handler for each and every anchor embedded in StackLayoutPanel
. It
iterates through the LayoutPanels and adds the handler to all MenuItems.
in.fins.client.widget/MenuBar.java
....
import java.util.Iterator;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.event.dom.client.ClickHandler;
....
public void addClickHandler(ClickHandler clickHandler) {
Widget widget = getWidget();
Iterator<Widget> it = ((HasWidgets) widget).iterator();
while (it.hasNext()) {
Widget w = it.next();
if (w instanceof LayoutPanel) {
Iterator<Widget> it1 = ((HasWidgets) w).iterator();
while (it1.hasNext()) {
Widget menuItem = it1.next();
if (menuItem instanceof MenuItem) {
((MenuItem) menuItem).anchor
.addClickHandler(clickHandler);
}
}
}
}
}
....
Link ContentPanel and MenuBar by calling addClickHandler()
method in
FinsShell.java
.
in.fins.client.content/FinsShell.java
....
import in.fins.client.widget.MenuBar;
....
@UiField
MenuBar menuBar;
public FinsShell() {
....
contentPanel.addTab("Home", new DateBox());
menuBar.addClickHandler(contentPanel);
}
....
With this, menu adds tab to ContentPanel.
This is the traditional way to handle events which directly couples
MenuBar and ContentPanel. EventBus
mechanism, introduced in GWT 2.1,
is an elegant way to decouple events source and handler. Revert back the
modifications done in this section i.e. remove ClickHandler
implementation in ContentPanel.java
, addClickHandler()
method in
MenuBar.java
and method call in FinsShell.java
, and we ready to look
at GWT EventBus.
GWT EventBus
GWT EventBus allows objects to interact without having direct
dependencies upon one another, and with this event sources no longer
require to maintain handler lists. Typically an application will have
one EventBus
, which carries the events broadcast by event sources.
Create a new packagein.fins.client.event
to
hold events and their handlers classes. First task is to create an
application wide EventBus
. Add EventBus.java
to
in.fins.client.event
package.
in.fins.client.event/EventBus.java
package in.fins.client.event;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.SimpleEventBus;
public class EventBus {
private EventBus() {
}
private static final SimpleEventBus INSTANCE = GWT
.create(SimpleEventBus.class);
public static SimpleEventBus get() {
return INSTANCE;
}
}
It is a singleton which return an instance of SimpleEventBus
.
Next we require MenuEvent
, that has to fire when user clicks
MenuItem
. Add MenuEvent
to in.fins.client.event
package. Eclipse
shows some error about MenuHandler which may be ignored as we are going
to add it in the next step.
in.fins.client.event/MenuEvent.java
package in.fins.client.event;
import com.google.web.bindery.event.shared.Event;
public class MenuEvent extends Event<MenuHandler> {
public static final Type<MenuHandler> TYPE = new Type<MenuHandler>();
private String menu;
public MenuEvent(String menu) {
this.menu = menu;
}
public String getMenu() {
return menu;
}
@Override
public Type<MenuHandler> getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(MenuHandler handler) {
handler.onMenuSelection(this);
}
}
extends Event<MenuHandler> and implements its two methods | |
Type associated with this event is MenuHandler | |
Type<MenuHandler> getAssociatedType() - Returns the Type used to register this event, allowing an EventBus to find handlers of MenuHandler class. | |
void dispatch(MenuHandler handler) – calls the handler and pass on the source MenuEvent . |
Next add MenuHandler interface, which extends EventHandler. Method
defined here is called in MenuEvent.dispatch()
. There is no
restriction in naming the method.
in.fins.client.event/MenuHandler.java
package in.fins.client.event;
import com.google.gwt.event.shared.EventHandler;
public interface MenuHandler extends EventHandler {
public void onMenuSelection(MenuEvent menuEvent);
}
Make ContentPanel
as MenuHandler with following modifications to
ContentPanel.java
.
in.fins.client.widget/ContentPanel.java
....
import in.fins.client.event.EventBus;
import in.fins.client.event.MenuEvent;
import in.fins.client.event.MenuHandler;
import com.google.gwt.user.datepicker.client.DateBox;
....
public class ContentPanel extends ResizeComposite implements MenuHandler {
public ContentPanel() {
initWidget(binder.createAndBindUi(this));
EventBus.get().addHandler(MenuEvent.TYPE, this);
}
....
@Override
public void onMenuSelection(MenuEvent menuEvent) {
String contentName = menuEvent.getMenu();
addTab(contentName, new DateBox());
}
}
ContentPanel
implements MenuHandler and its handler method retrieves
the name of the selected menu from MenuEvent
and adds a tab with
addTab()
method. In constructor, addHandler()
registers the
ContentPanel
with EventBus
to receive any MenuEvent
.
Now all that is left is to fire MenuEvents.
in.fins.client.widget/MenuItem.java
....
import in.fins.client.event.EventBus;
import in.fins.client.event.MenuEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
....
@UiConstructor
public MenuItem(String text, ImageResource imageResource,
final String contentName) {
initWidget(binder.createAndBindUi(this));
anchor.setHTML(getHtml(imageResource, text));
anchor.setName(contentName);
anchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
EventBus.get().fireEvent(new MenuEvent(contentName));
}
});
}
....
This adds ClickHandler
to the Anchor
which fires a new MenuEvent
with menu name to EventBus
. On click, MenuItem sends a MenuEvent to
EventBus, and it in turn, dispatches it to any interested handler i.e.
to ContentPanel and all these without any direct coupling between Menu
and ContentPanel.
GWT EventBus coding is slightly complicated as compared to event-listener and you may want to switch back to event-listener model. But hold that thought, once we get to the next widget benefits of EventBus become apparent. Throughout the application, we are going to use EventBus to handle events and to some its logic may still bit fuzzy, so to reinforce the concept let’s add Status widget to application.