In an attempt to orient myself and my colleagues with GWT, I've been reading up on GWT, trying things out, and blogging about them here. My goal is to start with simple use cases and build on to them with more complex ones. The way it's been working so far is that each post builds on the prior one.

Saturday, March 20, 2010

More Robust Back Button and Bookmark Support

In the last post, we looked at how to interact with GWT’s History class in order to provide Back Button and Bookmark support for the Contact List application. There are some issues with the approach we took in the last post. The biggest deficiency is that the page’s “place” is fully determined inside a particular UI Component. If the page was enhanced such that the page’s “place was represented by multiple UI Components, things could get messy. We have a call to History.newItem() inside the ContactListPresenter, so it will be really cumbersome if some other Presenter needs to participate in determining the current place in the application.

For example, if we enhance the Contact Management Application so that the user, from the Contact Detail UI Component, can decide if s/he wants to manage more information about the Contact:

image

When the user chooses to ‘Show More…’, a new UI Component will be presented for the user to view/edit other information about the Contact.

image

And let’s add support for the “Show More…” link to participate in our Bookmark/Back support. This means that a “place” in the Contact Management app. represents both the current contact selected in the Contact List UI Component AND the state of the show/hide link in the Contact Detail UI Component.

The design in our last post had the Contact List presenter calling History.addNewItem() when the user selected a Contact. When ‘Show More…’ is clicked, the URL needs to change again. If we were to call History.addNewItem(“ShowMore”), we’d be left with a URL like: ContactListTest.html#ShowMore. This wipes out the Contact that should also be in the anchor so, if the page were bookmarked, it wouldn’t know which Contact to display. In order for the Contact Detail UI component to manage this correctly, it would need to check the current URL and append ‘ShowMore’ on to it so that the URL instead looks something like this:

ContactListTest.html#Jim/ShowMore

By having the Contact Detail UI Component know what the Contact List UI Component put in the URL string, it starts coupling the two components together and it becomes harder to have Contact Details UI Component as a standalone, reusable UI Component.

And if the Contact List UI Component were to be reusable within some other page, it really shouldn’t call History.addNewItem(“Jim”) when a Contact is clicked. Something else on that page could already have something in there that needs to be maintained. Maybe it’s an email app. that enables the user to manage their Contacts. In this case, before the user even interacts with the Contact List, the URL would look something like: EmailApp.html#ContactManagement. And a link to Contact in the Contact List would then need to look something like: EmailApp.html#ContactManagement/Jim.

There are a ton of scenarios we could dream up, but the point is that for UI Component modularity, a UI Component’s Presenter probably shouldn’t be wholly responsible for generating its own URL’s.

To handle this, the Presenter will establish an interface for generating URL’s within its UI Component. Here’s an interface the ContactListPresenter establishes for its component.


/**
* Used to generate a URL for a Contact in the Contact List
*/
public static interface ContactListUrlGenerator{
public String generateContactUrl(Contact contact);
}


So when the ContactListPresenter is ready to generate a URL for a Contact in the Contact List, it will call in to the supplied implementation of this interface, get the URL, and then apply it to the View.



The ContactListPresenter will require a UrlGenerator at construct-time. Now the thing that constructs and wires up the Presenters can be responsible for implementing the URL generator. For the Contact Management application, here’s that generator:




private PlaceManager placeManager = new PlaceManager();

private class PlaceManager implements ValueChangeHandler, ContactListUrlGenerator{

public void onValueChange(ValueChangeEvent event) {
/*
* Pulls history token off URL and interprets it in order to tell the presenters
* in the app what to present.
*/
String contactName = event.getValue();
if (! contactName.equals("") && contactName != null){
// change the Contact List
Contact contact = contactListPresenter.showContactAsSelected(contactName);

// change the Contact Detail
detailpresenter.setContact(contact);
}
}

public String generateContactUrl(Contact contact) {
/**
* Helps generate the history token for the ContactListUrlGenerator
*/
return contact.getFirstName();
}
}


Our URL Generator implementation is actually more than just a URL generator. So it can listen to History events, it also implements ValueChangeHandler<String>. We put it all together and call it a PlaceManager. With this, we will use placeManager both to construct the ContactListPresenter and to register with the History class:




contactListPresenter = new ContactListPresenter(listView, placeManager);
History.addValueChangeHandler(placeManager);


The reason we give PlaceManager these two responsibilities is because they are so closely related. The generateUrl() method will be generating URL’s and onValueChange() will need to parse/interpret whatever generateUrl() constructs. If one method needs to change, the other will likely need to change, also. Lastly, by placing this URL management outside of the Presenters, it can actually have visibility in to all the Presenters and can do things like tell each Presenter what do with when the history token has a particular contact name:




// change the Contact List
Contact contact = contactListPresenter.showContactAsSelected(contactName);

// change the Contact Detail
detailpresenter.setContact(contact);


For the Contact Detail UI component, it would need to generate a link for its ‘Show More…’ link. It can define its own UrlGenerator to do so:




public static interface ContactDetailUrlGenerator{
public String generateShowMoreLink();
public String generateHideMoreLink();
}

Now our PlaceManager can implement ContactDetailUrlGenerator and help the Contact Detail UI Component be in the correct “place”.



In this post, we looked at how to bring “place management” out of the presenter and in to the AppController.

Followers