Using @ProxyEvent

A relatively recent feature of GWTP is @ProxyEvent, and at first glance it’s not obvious why it should be needed. This post explores how @ProxyEvent works and gives guidelines on when to use it and when to rely on the regular event registration mechanism. First, let’s look at some code that uses this annotation:

public class MessagePresenter extends Presenter<MessagePresenter.MyView, MessagePresenter.MyProxy> implements ShowMessageHandler {
  // ...
  @ProxyEvent
  @Override
  public void onShowMessage(ShowMessageEvent event) {
    getView().setMessage();
    placeManager.revealPlace(new PlaceRequest(NameTokens.messagePage));
  }
}

The first thing that’s really important to understand is that, even though @ProxyEvent appears in your presenter, the handler registration occurs within the proxy. In this case, it means that whenever the proxy intercepts a ShowMessageEvent it will instantiate the MessagePresenter and call its onShowMessage. At that point, the presenter is not necessarily visible, so the method reveals it by navigating with placeManager.revealPlace(…). If the presenter is not a place, you could also use forceReveal(). Be carfeul, however, since forceReveal() can lead to undesirable behavior when used on a place.

So when should you use @ProxyEvent? Basically, you use it every time that an event should have the ability to “wake up” your presenter. That is, whenever the presenter should be notified of an event even before it is first instantiated. This is why the method marked with a @ProxyEvent will often contain a mechanism to reveal the presenter.

These types of event handlers dramatically simplify the process of passing a large amount of information to a presenter before revealing it: bundle the information in the event, fire it, intercept it with a @ProxyEvent, and reveal the presenter inside the handler method.

Ok enough theory, let’s expand the previous example. The following code uses an event we already discussed in Gwt-Platform event best practices. The goal, now, is to navigate to MessagePresenter whenever the event is raised.

public class MessagePresenter extends
    Presenter<MessagePresenter.MyView, MessagePresenter.MyProxy> implements ShowMessageHandler {
  @ProxyCodeSplit
  @NameToken(NameTokens.messagePage)
  public interface MyProxy extends ProxyPlace<HomePresenter> {}

  public interface MyView extends View {
    void setMessage(String message);
  }

  final private PlaceManager placeManager;

  @Inject
  MessagePresenter(EventBus eventBus, MyView view, MyProxy proxy, PlaceManager placeManager) {
    super(eventBus, view, proxy);
    this.placeManager = placeManager;
  }

  @ProxyEvent
  @Override
  public void onShowMessage(ShowMessageEvent event) {
    getView().setMessage();

    placeManager.revealPlace(new PlaceRequest(NameTokens.messagePage));
  }

  @Override
  protected void revealInParent() {
    RevealContentEvent.fire(this, AppPresenter.TYPE_SetMainContent, this);
  }
}

Now every time a MessageEvent is fired, MessagePresenter will be shown. This works even though @ProxyCodeSplit loads this presenter asynchronously, and without having to couple presenters. The example below shows how PuzzleBazar uses a similar strategy to reveal its LinkColumnPresenter whenever it is needed:

public class LinkColumnPresenter extends Presenter<LinkColumnPresenter.MyView, LinkColumnPresenter.MyProxy> 
implements RevealDefaultLinkColumnHandler {

  public interface MyView extends View {}

  @ProxyStandard
  public interface MyProxy extends Proxy<LinkColumnPresenter> {}
  
  @Inject
  public LinkColumnPresenter(
      final EventBus eventBus, 
      final MyView view,  
      final MyProxy proxy ) {
    super(eventBus, view, proxy);
  }

  @Override
  protected void revealInParent() {
    RevealContentEvent.fire(this, SplitMainPresenter.TYPE_RevealSideBarContent, this);
  }
  
  @ProxyEvent
  @Override
  public void onRevealDefaultLinkColumn(RevealDefaultLinkColumnEvent event) {
    forceReveal();
  }
}

Ok, so now that you know how to use @ProxyEvent you may wonder why you shouldn’t always use it! The truth is that, in most cases, there is not much harm in using it to listen to an event. Sometimes, however, it may instantiate a bunch of presenters too early, momentarily slowing down your application. For example, imagine the following:

  // Bad example don't do this!
  @ProxyEvent
  @Override
  public void onNavigation(NavigationEvent event) {    
    if (event.getRequest().getNameToken().equals("logout")) {
      getView().setMessage( "Logout in process..." );
    }
  }

At first this may seem reasonable. However, it means your presenter will be instantiated as soon as a NavigationEvent is fired, even if this event has nothing to do with the “logout” place. If you have many such presenters, they will all be instantiated at once! In such cases, you probably want the presenter to register towards the handler in its onBind method:

  // The right way
  @Override
  protected void onBind(NavigationEvent event) {
    super.onBind();
    addRegisteredHandler(NavigationEvent.getType(), this);
  }
  @Override
  public void onNavigation(NavigationEvent navigationEvent) {
      getView().setMessage( "Logout in process..." );
  }

Another drawback of abusing @ProxyEvent is that it makes your proxy code bigger, and since all proxies need to be instantiated when your applications starts, this may increase the initial loading time. As a result the rule of thumb is to use @ProxyEvent whenever you need your presenter to “wake up” to an event, and to use addRegisteredHandler in your onBind() method for all other cases.

I hope this helps you better understand that neat little feature of GWTP. If you need more information, you can always look at Gwt-Platform’s wiki. See you soon!

3 comments

  1. Philippe Beaudoin · August 31, 2010

    Just a little detail we left out of the post but that is important: use either @ProxyEvent OR addRegisteredHandler, not both. Otherwise you’ll be receiving the event twice in some situations.

  2. Coolz · December 15, 2010

    if we use addRegisteredHandler and the presenter is aysnc , and havent loaded to the client browser. it can still listen for event?

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