Gwt-Platform event best practices (revisited)

Not too long ago, I started using GWT and GWTP and found it pretty hard to understand its event system.

I still can’t find much recent information about it, so I am writing this post to hopefully help others. Disclaimer: There many ways to achieve what I do in this post, but this is the way I prefer.

All right! Let’s demonstrate how events and the event bus work by creating a CSI-Hacking Dashboard. You can get the code on GitHub.

 

We have ComputerPresenter, a HackerPresenter and a root Presenter that holds/creates other widgets. We also have a ComputerHackedEvent and ComputerHackedHandler. The event will be raised to signal that the computers got hacked, and the handler code will be executed after that.

Here’s the classic implementation of a GWT event. Our event will only carry the hacker’s name.

I usually declare my handler as a nested interface in the event declaration. That’s only a matter of personal preference though.

Next we have to raise the event. That will be done by our HackerPresenter. A really simple way of becoming a computer hacker, is by adding a button to the screen and pressing it, right CSI?

Here’s the code of the view and the presenter.

So we have a button, and when we click on it, it raises an event. There are a couple of ways to raise an event, but the one I usually use, is the static fire() method on the event. (I’ll talk about the other ways of firing an event later on.)

Now we have to handle the event somewhere. We want to know when the computers get hacked, so we’ll represent the computers with the ComputerPresenter. Its role will be to print in the console when it gets hacked, and by which hacker. Here’s the presenter code:

This way, when a hacker clicks on the “start hacking” button, all the computers that are listening to the event will print something. As you can see, the ComputerPresenter registers itself as a handler for the ComputerHackedEvent through the addRegisteredHandler method.

This is a convenience method provided by GWTP. Using this method instead of registering directly on the EventBus will make the event registration part of GWTP’s lifecycle and unbind the event handler when the presenter is unbound. That means that if the presenter is unbound and rebound, you’ll have to re-register event handlers. This is why the onBind method is a good place to register handlers.

Here’s the code of the root presenter:

We just create one hacker named Zer0C00L and 2 computers to hack. And now the view:

 

Nice! When I click on “Hack the planet!” I see the following result in the Javascript console:

I got hacked. (A) by Zer0C00L
I got hacked. (B) by Zer0C00L

I should start writing in l33tsp33k now.

Now what if you remove one of the ComputerWidgets from the DOM by calling removeFromSlot(SLOT_COMPUTERS, computerB); and still try to hack the planet?

If you read the output of the console you will see:

I got hacked. (A) by Zer0C00L
I got hacked. (B) by Zer0C00L

Wait… What? The handler for the computer B is still registered, the presenter wasn’t unbound, it was only removed from the DOM.

What if we want computer B to stop listening to the events when it’s not present in the DOM? Well that’s a job for addVisibleHandler. So instead of registering the handler using addRegisteredHandler we’ll use addVisibleHandler that will handle this for us. This way, when a presenter is considered “not visible” in GWTP’s lifecycle perspective (read: not visible as in “visible in the DOM”), the event will not reach the handler. The new output should now be:

I got hacked. (A) by Zer0C00L

There’s still a problem though. What if there were too many computers for a single hacker? I think at some point we’ll have to add someone to the team. Let’s do it!

You should see 2 buttons saying “Hack the planet!” and when you click them both, the output is:

I got hacked. (A) by Zer0C00L
I got hacked. (B) by Zer0C00L
I got hacked. (A) by AcidBurn
I got hacked. (B) by AcidBurn

All computers are reacting to every hacker, which is not what we want. This is happening because of the way we registered the handlers earlier. What we want is for the computers to react a specific hacker’s ComputerHackedEvent.

Since we can have a reference to the said hacker, that is pretty easy to accomplish. We have to delegate the handler registration to the concerned presenter. From the RootPresenter we’ll delegate the task, but first let’s create an interface :

We can then let ComputerPresenter implement it.

Note that instead of registerHandler() you can also use registerVisibleHandler().

And finally, when you click on both buttons, the output should be:

I got hacked. (A) by Zer0C00L
I got hacked. (B) by AcidBurn

All right! We’re ready to hack the planet! Are we?

Remember when I said I would talk about the ways of firing events? If you are new to GWT and GWTP, you might have noticed that there are multiple methods available to fire events and register handlers.

Confused yet? I can say I was after seeing this. If you dig down you can see that a presenter widget gets an event bus injected and delegates most of its job to it. The only difference is that GWTP manages the handler registrations with its lifecycle (i.e: when a presenter gets unbound, the registered handlers get cleared). Also, if you dig for the fireEvent method, you’ll see GWTP delegates to eventBus.fireEventFromSource(). You may want to call the original fireEvent() if you want to match the following case (taken from the javadoc) “Fires the event from no source. Only unfiltered handlers will receive it”. Honestly, I’ve never faced that situation.

Here’s my cheat sheet of events in GWTP:

– Do I need to fire an event globally on the event bus? (i.e: everything registered to the event will handle it)
Y: presenterWidget.addRegisteredHandler() + SomeEvent.fire()
N: Go next

– Do I need to filter the handlers by the visibility of the handler?
Y: presenterWidget.addVisibleHandler() + SomeEvent.fire()
N: Go next

– Do I need specific handlers to handle events from a specific source?
Y: Create an interface called HasXXXHandlers and make your handling presenter implement it. GWTP gives PresenterWidget the capability to register handlers via registerHandler() and registerVisibleHandler(). Finally, fire the event with SomeEvent.fire()
N: That’s it. I usually don’t need more options, so the decision tree ends here. If you have another situation that doesn’t fit, let me know!

  • Great explanation. Thanks for this funny article!

    • Joel Trottier-Hebert

      Thanks! It was a lot of fun to write. 🙂

  • cellepo

    Why does ComputerHackedEvent extend GwtEvent instead of Event? GwtEvent javadoc (http://www.gwtproject.org/javadoc/latest/com/google/gwt/event/shared/GwtEvent.html) says, “There is no need for an application’s custom event types to extend GwtEvent. Prefer Event instead.”

    • Joel Trottier-Hebert

      GWTP uses `GwtEvent` instead of `Event`. I don’t think that’s a big deal for this demo, but it’d definitely be something to improve in GWTP.

    • But what about if you are using the gwt event bus pattern?