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!
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.
if we use addRegisteredHandler and the presenter is aysnc , and havent loaded to the client browser. it can still listen for event?
In those cases, you must use @ProxyEvent.
http://code.google.com/p/gwt-platform/wiki/GettingStarted?tm=6#Attaching_events_to_proxies