Gwt-Platform event best practices

A newer version of this blog post is available here.

As we worked through enhancing the way events were handled in Gwt-Platform, we came across one constatation, no common pattern was used or encouraged. This post addresses this issue and offers a little tutorial about how to create custom events in Gwt-Platform.

First of all, an event contains two major parts: the Event class itself and an Handler. Those two parts can either sit in different files or be included in the same file, with the Handler nested inside the Event. The second approach is our favorite since Handler is an interface related to an event. Also, it can be done in a few lines and reduce the total number of files.

Give that, here’s how we write our events:

public class ShowMessageEvent extends
    GwtEvent<ShowMessageEvent.ShowMessageHandler> {
  public interface ShowMessageHandler extends EventHandler {
    void onShowMessage(ShowMessageEvent event);
  }

  private static Type<ShowMessageHandler> TYPE = new Type<ShowMessageHandler>();

  public static void fire(HasEventBus source, String message) {
    if (TYPE != null) {
      source.fireEvent(new ShowMessageEvent(message));
    }
  }

  public static Type<ShowMessageHandler> getType() {
    return TYPE;
  }

  private final String message;

  public ShowMessageEvent(final String message) {
    this.message = message;
  }

  @Override
  public Type<ShowMessageHandler> getAssociatedType() {
    return TYPE;
  }

  public String getMessage() {
    return message;
  }

  @Override
  protected void dispatch(ShowMessageHandler handler) {
    handler.onShowMessage(this);
  }
}

Note here the static fire method. This is a convenient static factory method that let’s you fire an event without having to manually instantiate a ShowMessageEvent class. Using these methods is recommended rather than calling directly fireEvent. Also, the first parameter of this method is not the event bus itself, but rather the source. Passing in your presenter as the first parameter will make sure that the event is attached to the real source that triggered it. Using HasEventBus rather than GWT’s HasHandlers increases the type safety of GWTP, ensuring you never fire into a widget an event that was supposed to go on the bus.

Now that we have all the scaffolding for the event, it’s time to figure out how to use it. In Gwt-Platform every presenter implements HasEventBus so they all have the ability to fire events. The best practice approach is to simply use the static fire method of your event. In the above example this would be ShowMessageEvent.fire(this, “Hello world!”);. Some situations will require you to fire events from an object that doesn’t have access to fireEvent(). In this case, the best practice is to inject the EventBus, implement HasEventBus, and relay the call to fireEvent:

public abstract class MyCustomCallback<T> implements AsyncCallback<T>, HasEventBus {
  @Inject
  private static EventBus eventBus;

  @Override
  public void onFailure(Throwable caught) {
    ShowMessageEvent.fire(this, "Oops! Something went wrong!");
  }

  @Override
  public void fireEvent(GwtEvent<?> event) {
      eventBus.fireEvent(this, event);
  }
}

In this case we have used static injection to provide the EventBus (don’t forget to requestStaticInjection in your Gin module!). This is because AsyncCallback classes do not typically participate in dependency injection. If your class does participate in dependency injection then you should use constructor injection. The goal of this pattern is to ensure that event sources are correctly tracked. Since the callback implements HasEventBus it will set itself as the source of the event. More importantly, every class that extends this abstract class will be able to fire any event and set itself as the source.

Now, how do we listen to events travelling on the bus? If you’re listening from a presenter, the prefered way is to use addRegisteredHandler(eventType, eventHandler);. This method not only registers a handler on the event bus, it also makes sure that every registered handler is correctly unregistered when the presenter is unbound. If you are not within a presenter, inject the EventBus and call eventBus.addHandler(eventType, eventHandler);. It is customary to use an anonymous inner class as an eventHandler, for example:

public abstract class MyPresenter extends PresenterImpl<MyPresenter.MyView, MyPresenter.MyProxy> {
  ...
  @Override
  public void onBind() {
    super.onBind();
    addRegisteredHandler( ShowMessageEvent.getType(), new ShowMessageHandler() {
          @Override
          void onShowMessage(ShowMessageEvent event) {
            getView().displayMessage( event.getMessage() );
          }
        } );
  }
}

But you can also use the following pattern:

public abstract class MyPresenter extends PresenterImpl<MyPresenter.MyView, MyPresenter.MyProxy>
    implements ShowMessageHandler {
  ...
  @Override
  public void onBind() {
    super.onBind();
    addRegisteredHandler( ShowMessageEvent.getType(), this );
  }

  @Override
  void onShowMessage(ShowMessageEvent event) {
    getView().displayMessage( event.getMessage() );
  }
}

I hope this post will help you design your own events, but in any case feel free to leave comments or ask questions! In a future article we will discuss how you can automatically generate most of the boilerplate using GWTP’s annotation processing classes contributed by Brendan Doherty. We will also discuss the use of the @ProxyEvent annotation and how it differs from the events described above.

13 comments

  1. Flori · August 30, 2010

    Thanks for your great post Christian. Maybe you should also mention the annotation processing implemented by Brendan which saves a lot of boilerplate (in relation to http://code.google.com/p/gwt-platform/wiki/BoilerplateGeneration). It’s a great feature.

    @GenEvent
    public class ShowMessage {
    String message;
    }

    should be enough instead of ShowMessageEvent 😉 greetings!

    • Christian Goudreau · August 30, 2010

      Yeah, well we wanted to make two articles, we don’t want to enforce anyone to use our annotation processing tool 😀

      There’s also more to his tool, so I’ll make sure to talk about all features 🙂

      Cheers,

  2. rainer198 · October 23, 2012

    Hi, although this post is quite old now, I hope that comments are still read:

    I have two presenters which are handlers for the same event type. At one time, only one presenter is displayed, the other is hidden.

    I have registered the handlers as described above: with addRegisteredHandler() . However, unlike expected (quote “it also makes sure that every registered handler is correctly unregistered when the presenter is unbound”) the event is always dispatched to both presenters. I also could not find any code which unregisters event handlers triggered by Presenter lifecycle methods.

    Did I misunderstood something

    • Christian Goudreau · October 23, 2012

      Comments are always read, I get notified when there’s new ones 😛

      Did you specifically call unbind? Presenters are never unbound unless you do it manually. They are singletons that lives and die with the application unless you specify otherwise.

      • rainer198 · October 23, 2012

        Oh, really quick indeed 😉

        No, I did not call unbind(), probably I just mixed up the lifecycle state names. However, if I do not want to unbind, what would you suggest to do to achieve that the event is only dispatched to the displayed presenter without manually unregistering? And if only manual unregistering is possible, which lifecycle method would you recommend to do this?

      • Christian Goudreau · October 23, 2012

        Normally only a presenter that has a handler to that specific event would receive it. The EventBus only calls the handler that is registered with the event.

        That being said, if your concern is about the presenter receiving event when it’s not visible, you could call unbind in the onHide method. Afterward when it’ll get displayed again, onBind() will be called again.

      • rainer198 · October 23, 2012

        Probably a comment-tree-depth constraint 🙂

        Yes, I will follow your advice although it does have a smell that I do something for it that usually is not done (unbinding a presenter).

        Thanks for your help and your MVP library at all,
        Best regards,
        Rainer.

  3. Nicolas Michaud · May 27, 2013

    Bonjour,

    J’ai dans mon “MainPagePresenter” contient trois presenters/slots: header, content et footer. Le header contient différents onglets pour choisir les différentes pages (et donc changer le contenu principal). Je veux changer le style de ces onglets pour indiquer la page active. Est-ce que ce serait adéquat d’utiliser le méchanisme décrit ici pour faire cela en faisant le fire sur le onreset() des presenters de contenu et de mettre le handler correspondant dans le presenter du header pour aller changer le style ?

    • Christian Goudreau · May 28, 2013

      Si le header est un presenterwidget, vous pouvez effectivement utiliser onReset et utiliser le PlaceManager pour aller chercher la place active. C’est ce que je fais à plusieurs occasions et même, si chacun des liens sont des presenters widgets, vous pourriez les laisser ce gérer d’eux même sans interaction de la part du header parent.

      La deuxième méthode est souvent ce que j’utilise quand je fais des tab / tab dynamique.

  4. Jan · June 22, 2013

    Hello,

    how can i fire events form server side? is there any possibility to use gwteventservice with gwt-platform?

    • Christian Goudreau · June 25, 2013

      Yes it is, another solution would be to use the great Errai-Bus!

      • Jan · June 25, 2013

        Can you give me a clue how?

      • Christian Goudreau · June 25, 2013

        unfortunately, I do not have any. Stackoverflow may be a good place to ask the question as I’ve never tryed those tools. GWTP let you use whatever communication based system you wish to use, it shouldn’t be a problem.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s