Navigating to a new view with gwt-presenter, part 2
Posted by David Chandler on October 1, 2009
In the previous post, we considered how to switch between views using a DeckPanel. Depending on the situation, one possible disadvantage to a DeckPanel is that it retains each member view widget in memory even when not displayed. If instead you want to remove a view widget from its container when transitioning to another view, you can use gwt-presenter’s WidgetContainerPresenter and WidgetContainerDisplay classes. Here is the same example shown with these classes in which we use a VerticalPanel as a container and navigate from the ManageListsPresenter/View to the AddPrayerListPresenter/View within the VerticalPanel. Note that the ContentContainerPresenter constructor calls showPresenter(). This is very important, as our view’s implementation of showWidget() calls clear() and add() to replace the existing contents of the VerticalPanel with the new widget. If you don’t call showPresenter() in the container constructor, then the default behavior of WidgetContainerPresenter is to add all the member presenters to the view.
package com.roa.client.presenter; import net.customware.gwt.presenter.client.EventBus; import net.customware.gwt.presenter.client.place.Place; import net.customware.gwt.presenter.client.place.PlaceRequest; import net.customware.gwt.presenter.client.widget.WidgetContainerDisplay; import net.customware.gwt.presenter.client.widget.WidgetContainerPresenter; import net.customware.gwt.presenter.client.widget.WidgetPresenter; public class ContentContainerPresenter extends WidgetContainerPresenter<ContentContainerPresenter.Display> { private static final Place PLACE = new Place("content"); public interface Display extends WidgetContainerDisplay { } public ContentContainerPresenter(Display display, EventBus eventBus, WidgetPresenter<?>[] presenters) { super(display, eventBus, presenters); bind(); // Show first presenter as default showPresenter(presenters[0]); } @Override public Place getPlace() { return PLACE; } @Override protected void onPlaceRequest(PlaceRequest request) { // TODO Auto-generated method stub } }
And here is the matching view that uses a VerticalPanel as the container widget:
package com.roa.client.ui.web.content; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.roa.client.presenter.ContentContainerPresenter.Display; public class ContentContainerView implements Display { private final VerticalPanel contentPanel = new VerticalPanel(); public ContentContainerView() { } @Override public void addWidget(Widget widget) { contentPanel.add(widget); } @Override public void removeWidget(Widget widget) { contentPanel.remove(widget); } @Override public void showWidget(Widget widget) { contentPanel.clear(); contentPanel.add(widget); } @Override public Widget asWidget() { return contentPanel; } @Override public void startProcessing() { // TODO Auto-generated method stub } @Override public void stopProcessing() { // TODO Auto-generated method stub } }
These are wired up in AppPresenter and the view change triggered with a PlaceRequestEvent just as shown in the previous post.
Dun said
Hi David,
i’am also using gwt-presenter on my projects. And I also use a kind of navigation as described here. A NavigationManager is listening for place change, and call a “changeContent(WidgetPresenter)” on my mainPresenter.
But I just found that the old presenter is not removed from memory. For example if the presenter have an eventBus.addHandler method listening on a special event and showing a Window.alert(“message”);, if you go back and forward on the same presenter twice you will have 2 messageboxes for the same event, back and forward again and you will have 3 …
Did you experiment the same problem, or am I missing something ?
Best Regards
Dun
David Chandler said
Correct. Presenters are singletons and are not automatically unloaded. So any code you put in the constructor (or bind(), if you’re calling it from the constructor) will be executed only once, whereas any code in onPlaceRequest() will be executed each time you navigate to that place. I use onPlaceRequest() to clear the view and/or preload it with new data based on the parameters in the PlaceRequest.
Swapping DIVs technique for navigation with gwt-presenter « TurboManage said
[…] 21, 2009 I have previously written about navigation in gwt-presenter using the DeckPresenter or WidgetContainerPresenter approaches. Those techniques work well for swapping out views in one portion of the page, and could […]
mxmtycoder said
Hi, I’ve been following your posts about gwt-presenter and I found them very useful.
Thanks for sharing the knowledge.
I wondered how the placemanager work with history management?
I played a little bit with apache hupa gwt mail client (which is using gwt-presenter & gwt-dispatch)and I noticed that is not handling history, is this an issue from Hupa implementation or from the gwt-presenter?
Thanks,
Pablo
David Chandler said
Thanks, Pablo, glad to help. PlaceManager essentially synchronizes the browser’s notion of place with gwt-presenter’s. It does this by listening to two events: 1) PlaceManager registers a handler for a ValueChangeEvent from History, such as when the user clicks the back button or replaces the URL in the address bar, and fires a PlaceRequestEvent in response. And 2) PlaceManager registers a handler for PlaceChangedEvents fired manually in your code and calls History.newItem to mark the place in the browser.
I’m having good success with gwt-presenter keeping track of history, so I suspect the Hupa implementation may not be firing PlaceRequestEvents or PlaceChangedEvents in all the places you would expect. The code must fire one of these events every “place” that it wants GWT to mark for bookmarkability, etc.
Anonymous said
Strong site
Per Wiklander said
I’m trying to implement this. You say (emphasis mine):
Isn’t that going to happen anyway? The result being that each view is added and then cleared away when you do
showPresenter(presenters[0]);
to get back to the view you actually wanted. At least this is what it looks like to me when inspecting theonBind()
method ofnet.customware.gwt.presenter.client.widget.WidgetContainerPresenter
. Perhaps we should override onBind() to not add all the widgets in the first place? Or just do a noop inaddWidget()
inContentContainerView()
. Or is that method needed for anything else? One more thing:showPresenter(presenters[0]);
could be replaced witheventBus.fireEvent(new PresenterRevealedEvent(defaultPresenter));
in AppPresenter, right?As you see I’m unsure of things, but that’s why we ask questions π
David Chandler said
Exactly right on all counts. In my latest code, I do not call showPresenter() within ContentContainerPresenter, and I fire a PresenterRevealedEvent (a PlaceRequestEvent, actually, which in turn fires a PresenterRevealedEvent) within AppPresenter instead.
I see no problem with overriding WidgetContainerPresenter.onBind() as long as you include the code to register the event handler. It looks you could get away with a noop in addWidget(), as the PresenterRevealedEvent handler calls showWidget() rather than addWidget(). But because it’s defined in the WidgetContainerDisplay interface, there’s always the risk that something in gwt-presenter could call it in a future release. Either way, you’ll just have to be alert to the potential for compatibility problems with future releases.
Good questions / clarifications.
Per Wiklander said
I’ll have to put those optimizations on the “things to consider” list. Right now I think it’s best to concentrate on getting better at GWT as a whole and stick to what seems to work. I would hate to have to hunt down a bug that is due to me changing something in the framework when I should be producing widgets and getting interesting data from the server π
lost said
what about revealDisplay ? and what to put in revealDisplay vs onRevealDisplay, and how they are different than showPresenter you used ?
laura said
Hi, excellent tutorial… I have a question related to the places… Suppose I have a presenter that I can use several time for CRUD scenaries… It is possible to add several places to the same presenter????
David Chandler said
Thanks, Laura. I think you’d want a Place for each Presenter, but you could add a request param that the one place/presenter could use to distinguish among different CRUD screens.
laura said
Hi David, thanks a lot for your quick reply… I manage to distinguish the different CRUD screens passing the arguments to the presenter through the places…everything works fine until the history get activated…everytime I push the back button is a mess…the history does not send me to the last place, it send me to another place that hold this CRUD presenter, so I think that at the end I have to stick with one presenter per place(wich is a pitty)… of course this presenter will be an extention of my crud presenter, am I right???