TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and Google Cloud Platform Instructor now residing in Colorado. Besides tech, I enjoy landscape photography and share my work at ColoradoPhoto.gallery.

  • Subscribe

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 224 other subscribers
  • Sleepless Nights…

    October 2009
    S M T W T F S
     123
    45678910
    11121314151617
    18192021222324
    25262728293031
  • Blog Stats

    • 1,040,365 hits

Archive for October 1st, 2009

I *like* code like this: easiest possible navigation with gwt-presenter

Posted by David Chandler on October 1, 2009

Here’s a super easy way to trigger a view transition and pass a parameter using gwt-presenter:

...
import net.customware.gwt.presenter.client.place.PlaceRequest;
...
		PlaceRequest placeRequest = new PlaceRequest(ManageListsPresenter.PLACE)
			.with(ManageListsPresenter.PARAM_TEST, "34")
			.with(ManageListsPresenter.PARAM_ANOTHER, "hello");
		Hyperlink my_lists = new Hyperlink("My Lists", placeRequest.toString());

That’s it! No ClickHandlers needed, and gwt-presenter takes care of all the rest.

The PlaceRequest.with() method is a nice touch to gwt-presenter. It all ends up on the URL, which is important for GWT’s History mechanism, and it’s a clean way to generate the URL. Of course, there are corresponding getParameter methods to extract passed parameters from a PlaceRequest, which you would typically do in the presenter’s onPlaceRequest() method.

So what happens when the user clicks the link? The view associated with the requested presenter is shown, the place is marked in GWT’s History mechanism, and the presenter’s onPlaceRequest() method is invoked. No ClickHandlers on the Hyperlink are required to make this happen. When the user clicks a Hyperlink, GWT fires a ValueChangedEvent on History, which is picked up by gwt-presenter and sets off the chain reaction. Even if the requested presenter belongs to a container (WidgetContainerPresenter) that is not currently shown, gwt-presenter calls revealDisplay() on the presenter and its container if needed to bring it to the front.

If you want to use a Button, Label, etc., instead of Hyperlink, you can simply add a ClickHandler and fire the same PlaceRequestEvent that gwt-presenter uses under the covers in response to a Hyperlink:

		display.getAddButton().addClickHandler(new ClickHandler()
		{
			@Override
			public void onClick(ClickEvent event)
			{
				PlaceRequest placeRequest = new PlaceRequest(AddPrayerListPresenter.PLACE);
				eventBus.fireEvent(new PlaceRequestEvent(placeRequest));
			}
		});

Note: the browser only updates the URL in the address bar in response to a Hyperlink. If you use a Button or other ClickHandler and want to update History and the URL in the address bar, you must also fire a PlaceChangedEvent(), typically in the onPlaceRequest() method that gets called by the original PlaceRequestEvent(). This results in a call to GWT’s History.newItem().

	@Override
	protected void onPlaceRequest(PlaceRequest request)
	{
		// necessary when we've fired PRE from code vs Hyperlink
		eventBus.fireEvent(new PlaceChangedEvent(request));
	}

Long-time Web developers will of course recognize that the basic Hyperlink as created above is the same as we’ve been doing for 15 years now. The beauty of gwt-presenter is that with the same simple URL construction, you can easily target one section of the page for dynamic update, and do it in a way that works with browser history and bookmarks to boot. There might be something to this GWT thing.

Advertisement

Posted in Google Web Toolkit, Model-View-Presenter | 11 Comments »

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.

Posted in Google Web Toolkit, Model-View-Presenter | 13 Comments »

Navigating to a new view with gwt-presenter

Posted by David Chandler on October 1, 2009

How can you move to a new “view” with gwt-presenter? In GWT, you wouldn’t ordinarily replace the entire page as in a server-side Web app. Suppose you have one area of the screen, say, a VerticalPanel, in which you want to show any number of functional views (i.e, manage lists, edit list properties, etc.). One way to do this is using a DeckPanel, which is the base class behind GWT’s TabPanel widget. DeckPanel has no visible UI controls, but lets you programatically swap out one widget for another just as if you were clicking tabs. Gwt-presenter provides DeckPresenter and DeckDisplay to help with just that. Here’s a sample DeckPresenter class.

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.DeckDisplay;
import net.customware.gwt.presenter.client.widget.DeckPresenter;
import net.customware.gwt.presenter.client.widget.WidgetContainerDisplay;
import net.customware.gwt.presenter.client.widget.WidgetPresenter;

public class ContentContainerPresenter extends DeckPresenter
{

	private static final Place PLACE = new Place("content");

	public interface Display extends WidgetContainerDisplay
	{
	}

	public ContentContainerPresenter(DeckDisplay display, EventBus eventBus,
			WidgetPresenter<?>[] presenters)
	{
		super(display, eventBus, presenters);
		bind();
		// One way to show default presenter--see also AppPresenter below for another way
		// showPresenter(presenters[0]);
	}

	@Override
	public Place getPlace()
	{
		return PLACE;
	}

	@Override
	protected void onPlaceRequest(PlaceRequest request)
	{
		// TODO Auto-generated method stub
	}

}

And here’s the corresponding view:

package com.roa.client.ui.web.content;

import net.customware.gwt.presenter.client.widget.DeckDisplay;

import com.google.gwt.user.client.ui.Widget;

public class ContentContainerView extends DeckDisplay
{

	public ContentContainerView()
	{
	}

	@Override
	public void addWidget(Widget widget)
	{
		super.add(widget);
	}

	@Override
	public void removeWidget(Widget widget)
	{
		super.remove(widget);
	}

	@Override
	public void showWidget(Widget widget)
	{
		// Refer up to DeckPanel
		super.showWidget(widget);
	}

	@Override
	public Widget asWidget()
	{
		return this;
	}

	@Override
	public void startProcessing()
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void stopProcessing()
	{
		// TODO Auto-generated method stub

	}

}

Gwt-presenter’s DeckDisplay, unlike the other gwt-presenter interfaces named “Display”, is actually a concrete class that extends GWT’s DeckPanel. As such, we can use it as our view implementation directly. Now let’s wire them up. Because DeckPresenter’s constructor takes an array of presenters as an argument, there’s no way (that I know of, at least) to instantiate it with GIN using @Inject. So in our AppPresenter, we manually construct the needed classes like this:

	@Inject
	private EventBus eventBus;
	@Inject
	private ManageListsPresenter listsPresenter;
	@Inject
	private AddPrayerListPresenter addPresenter;
...
		// Instantiate container presenter manually as its constructor
		// prevent GIN binding
		WidgetPresenter<?>[] presenters = new WidgetPresenter<?>[]
		{ listsPresenter, addPresenter };
		ContentContainerView contentContainerView = new ContentContainerView();
		ContentContainerPresenter contentContainerPresenter = new ContentContainerPresenter(
				contentContainerView, eventBus, presenters);
		// Fire event to reveal only default presenter
		eventBus.fireEvent(new PresenterRevealedEvent(listsPresenter));

In order to show the default presenter (in this case, Manage Lists), we fire a PresenterRevealedEvent after constructing the ContentContainerPresenter. Without it, however, the default behavior of GWT presenter is to show the widgets associated with all presenters that have been added to the container.

Finally, we need to fire an event to transition from one view to another. We could use a PresenterRevealedEvent; however, the PlaceRequestEvent does much more. Gwt-presenter’s BasicPresenter class (from which WidgetContainerPresenter and DeckPresenter are derived) listens for PlaceRequestEvents, marks the Place in History, calls onPlaceRequest() for the requested presenter, and finally calls the requested presenter’s revealDisplay() method, which in turn fires a PresenterRevealedEvent.

The code below will trigger a transition from the ManageListsPresenter to the AddPrayerListPresenter when the add button is clicked. The easiest way to get a reference to the presenter we’re going to is simply to make its PLACE constant public so we can use it in PlaceRequests from other classes.

public class ManageListsPresenter extends
		WidgetPresenter<ManageListsPresenter.Display>
{
		display.getAddButton().addClickHandler(new ClickHandler()
		{
			@Override
			public void onClick(ClickEvent event)
			{
				PlaceRequest placeRequest = new PlaceRequest(AddPrayerListPresenter.PLACE);
				eventBus.fireEvent(new PlaceRequestEvent(placeRequest));
			}
		});
...
}

Posted in Google Web Toolkit, Model-View-Presenter | 3 Comments »

 
%d bloggers like this: