Translation available in: French.
Updated 2010-10-16: updated to GWT 2.1.0.
I previously described the core of GWT 2.1 Places and will now focus on the optional features.
User confirmation before navigating
In many applications, you want to have confirmation of the user before navigating away; this is mostly used when the user made changes and didn't yet save them. This can be easily accomplished with GWT 2.1 places: you'll register a
PlaceChangeRequestEvent.Handler on the event bus and call the
setWarning method with a non-null value that will be used to ask confirmation to the user.
goTo on the PlaceController, before changing the current place, it actually first fires a
PlaceChangeRequestEvent. Once the event has been dispatched to all the handlers, the current place will actually be changed and the
PlaceChangeEvent fired only in two cases:
- the event's warning property is null, or
- the event's warning property is non-null and the user has confirmed the navigation. Confirmation is asked using a (browser-native) dialog box, with the event's warning property value used as the message presented to the user.
Additionally, when navigating away or closing the window or tab (i.e. quitting the application), such a
PlaceChangeRequestEvent is also fired (with the next place set to
Place.NOWHERE), and setting a non-null warning value will also ask the user confirmation (for those interested in the gory details, this is accomplished by setting the
Integration with RequestFactory
GWT 2.1 does not come with built-in direct integration with
RequestFactory. You'll find a preview of what'll come in a later release in the code for the Expenses sample, in the form of two concrete places that can help you when a place is tied to a
EntityProxy instance or type: the
ProxyListPlace represents the list of records of a given type, while the
ProxyPlace models an operation (create, show details, or edit) on a particular record instance.
Integration with the browser's history
Integration with the browser's history means:
- serializing the current place to the URL so it can be bookmarked and creating an entry in the browser's navigation history; and
- deserializing the URL back to a place, and fire the appropriate events to the event bus to finally update the current place.
#1 is done when the place changes due to a call to the PlaceController's goTo, while #2 is triggered by the browser when the user either navigates in its history through the browser's "previous page" and "next page" buttons, or loads a previously bookmarked URL generated by #1.
To use this feature, you'll first create
PlaceTokenizers to map history tokens to/from places, and then an (empty) interface, extending
PlaceHistoryMapper and annotated with
@WithTokenizers to declare the
PlaceTokenizers to be used, that you'll
GWT.create() and finally register, through a
PlaceHistoryHandler, with the PlaceController, the EventBus and a default place (the place to use when there's no token, or when the token cannot be mapped to a place).
How does the PlaceHistoryHandler maps history tokens to places?
Each PlaceTokenizer is associated with a token prefix using a
@Prefix annotation. The PlaceHistoryHandler splits the history token on the first colon, compares the left-hand part to the prefixes it knows, and then GWT.create() the matched PlaceTokenizer class and call its
getPlace method with the right-hand part as the argument.
How does the PlaceHistoryHandler maps places to history tokens?
PlaceTokenizer is a generic class actually defined as
PlaceTokenizer<P extends Place>. When the code generator runs on the PlaceHistoryHandler, it examines the PlaceTokenizers and extracts their
P generic type argument. This information is then used at runtime to map a place to the appropriate PlaceTokenizer class, so that the PlaceHistoryHandler can GWT.create() an instance of it and call its
getToken method. The returned value will them be prepended with the PlaceTokenizer's @Prefix and a colon as a separator, and the resulting String is used as the history token.
Using a factory for your PlaceTokenizer
There are times where your PlaceTokenizer has a dependency on other objects, but there's no place for customization in the above algorithm, instantiating a PlaceTokenizer every time they need one and throwing it away right after that. Here comes the
PlaceHistoryHandlerWithFactory: you'll inherit from this interface instead of PlaceHistoryHandler and then call
setFactory with a factory (or provider) of PlaceTokenizers right before initializing it.
PlaceHistoryHandlerWithFactory takes a generic type argument, which the code generator will introspect to collect all zero-argument methods whose return type is assignable to PlaceTokenizer. The generated class will call these methods instead of using GWT.create() when it needs a PlaceTokenizer; the methods can also be annotated with @Prefix to override the annotation possibly present on the PlaceTokenizer subclass; finally, PlaceTokenizer classes that your factory can provide mustn't be specified in the @WithTokenizer annotation, which therefore becomes optional.
Your factory can really be any class or interface, which means you can even use a
Ginjector if you're using GIN for dependency-injection.
Using your own mapper, without PlaceTokenizer
You're actually not required to
ProxyHistoryMapper, and rely on the generated code based on
PlaceTokenizers, you can very well implement the interface yourself, the way you want it. And not only it is technically possible, but it is a supported use case (believe me on this one, I'm the one who submitted the patch).
The next article will be about GWT 2.1 activities, which sits above places, abstracting events' handling, and is probably what the GWT team call the "MVP framework" even though, actually, it helps in doing MVP but doesn't enforce its use, and you'll probably also need to build MVP components that are not activities… Still, GWT 2.1 activities are a wonderful API to work with, stay tuned!