Tuesday, September 13, 2011

A Complex-Layouting Library over mvp4g

Hello, mvp4g blog readers! shaman.sir is speaking.

With gratitude to Pierre, I am here to present you an mvp4g extention that will allow you to build a portlet-oriented web-applications with mvp4g.

To provide you with quick example, here's a running demo: http://gwt-mvp4g-layouting-demo.appspot.com/

And here's the short summary of documentation pages:

mvp4g framework helps in writing applications which has not a lot of significant changes happening in the inner page section or just with a central part that is /instantly/ replaced with a new content. I can’t say that it is a limitation of the framework — this situation may change in near future or, in fact, it is may be not so required for a lot of users — however, for the moment, this fact takes place and it was required for us in developing experika.


This library listens for every history event that happens in your application, finds what widgets layout to use for current page, and if it needs to rebuild the page, it rebuilds it and fills with new widgets (and if just part of page needs to be redrawn, it redraws only this concrete part). And only then it calls an appropriate navigation event. When you describe the layout, you just specify the placeholders for widgets, and there are special LayoutBuilders who fill the layout with widgets depending on current page and its state (it is true, support states like Loading/NothingFound/NoMatches/SomethingFound, you can easily switch them from inside the Presenters).


A library is in the stage of discovering final bugs and plans on improvement, so if you'll find a bug or want to offer some good idea, please issue it.

Here the links go:


Also, I need to mention people who helped me with this framework:
  • Vitaly Gashock, who introduced mvp4g to me and who gave me the idea to make "everything like portals" :).
  • Sergey Dyniovsky, who gave advices on how it will be easier to apply a CSS markup to resulting pages






Thursday, August 11, 2011

Custom Place Service

History management is probably one of the features with the most impact on user's experience and on your application architecture. In Mvp4g, it is also one of the most customizable features. This feature is organized around a Place Service and since 1.3.0, you can easily set your own. You can then override 100% of the default place service or you can just change part of it to manage history exactly the way you want.

In this tutorial, we're going to cover one of the most common cases to set a custom place service, changing characters used in the URL to separate the event name from the parameters. We will reuse the example from the previous post and add a custom place service. You can download the example here (Project: Mvp4g_Custom_Place_Service).

Creating a custom Place Service

To change the parameter that separates the event name from the parameters in the URL, all you have to do is create a class that extends PlaceService and override the getParamSeparator method.
public class CustomPlaceService extends PlaceService {

    protected String getParamSeparator() {
        return ":";
    }

}

Setting a custom Place Service

To set a custom place service, you need to annotate your event bus with @PlaceService and set the class of your place service.
@PlaceService( CustomPlaceService.class )
@Events( ... )
public interface CustomPlaceServiceEventBus extends EventBusWithLookup {
    ...
}
Mvp4g will then use this class to instantiate it.

You have now created and set a custom place service that will replace the default place service.

Particular case: “/”

You can use “/” to separate event name from the parameters but an extra step is required. This character is a particular case because it is also used to manage child modules history. To sum up, when you store an event of a child module in the history, the child module name followed by “/” is added to the event token. By default, you then have something like this:
childModuleName/eventName?parameters
So, if you set the “/” to separate event name from the parameters, your token looks like this:
childModuleName/eventName/parameters
If the event doesn't have any parameters, you will then have the following token:
childModuleName/eventWithNoParameterName
The issue here is this token won't be interpreted the way it is expected. Since there is only one “/” in this token, Mvp4g will assume it is used to separate parameters from the eventName so it will parse 'childModuleName' as the event name and 'eventWithNoParameterName' as the parameters.
To solve this issue, we need to make sure a “/” is always added to the token after the event name even if there is no parameter. Thus the previous token becomes:
childModuleName/eventWithNoParameterName/
To do this, we're going to modify our custom place service to change the default behavior. The PlaceService object uses a 'tokenize' method that generates a token from the event name and its parameters string representation. You can then override this method to automatically add “/” after the event name.
@Override
public String tokenize( String eventName, String param ) {
    //always add the paramSeparator since "/" is used 
    //for module separator and paramSeparator
    String token = eventName + getParamSeparator();
    if ( ( param != null ) && ( param.length() > 0 ) ) {
        token = token + param;
    }
    return token;
}
We now have created a custom place service where “/” is used as a separator.

What's next?

This tutorial is quite short and simple but it covers, I believe, one of the most important features to be aware of. Mvp4g provides a default mechanism to generate and retrieve the history token but it's entirely up to you to have the one you want. The part you have to override will depend on the changes you want to bring. If you want to modify the entire logic, you will probably have to override the 'convertToken' and 'place' methods. If you'd just like to alter the default generated token, you will most likely be looking at the 'tokenize' and 'parseToken' methods.

You can also create a custom place service not to modify Mvp4g logic but to add another action when the token changes. For example, you could easily add Google Analytics to your application:
public class CustomPlaceService extends PlaceService {
    ...    
    public String place( String eventName, String param, boolean onlyToken ) {
        //send info to Google Analytics
        sendToGoogleAnalytics(eventName);
        
        return super.place( eventName, param, onlyToken );
    }
}
This is probably a good example to develop in more details in a future post.

Wednesday, July 20, 2011

Hyperlink Navigation

Building your application navigation with hyperlink can improve user's experience. He can then navigate the same way through your website as he would on a non-ajax website. Hyperlink gives him access to familiar link functionalities like the over state to display the link in the status bar or the right click to open the link in a new tab or copy it.

One feature introduced with Mvp4g-1.3.0 is the hyperlink token generation, which easily creates the token associated to an event. In this tutorial, we're going to modify our previous example to use hyperlinks for our menu. You can download the example here (Project: Mvp4g_Hyperlink_Navigation).

Impact of Hyperlink Navigation


Using hyperlink will impact how you can build your navigation. The first main impact is when you retrieve parameters associated with your navigation. With regular navigation, you get your parameters after the user's action to navigate. The most common case is that you retrieve your parameter in the click handler after the user clicks on your menu.
On the other hand, you build your token for the hyperlink before you display it. You will then need to retrieve your navigation parameters before user's action. With hyperlink token, you will also have to decide when to build your token. Is this token static? Then you can build it once, when you build your hyperlink widget. If not, you may have to build it every time you display it.

The second main impact concerns Mvp4g and how the navigation is going to be treated. When you use regular navigation, you usually fire an event that is forwarded to a handler. With hyperlink navigation, you actually change the history token. This change is handled by the GWT History class that forwards it to the Mvp4g Place Service, which then uses a history converter to fire the appropriate event (which is then forwarded to the handlers). Thus with regular navigation, you only use the event bus whereas with hyperlink navigation, you go through the whole history process. I tried to summarize these differences in the following sequence diagrams:

Sequence Diagram for regular navigation
Sequence Diagram for hyperlink navigation


Changing the event bus to generate token


To generate a token for an event, it is quite easy thanks to the token generation feature introduced in Mvp4g-1.3.0. All you have to do is change the return type of the event method in your event bus interface from void to String. You can generate a token only for events associated with a history converter.

In our example, we need to generate tokens for goToPage1 and goToPage2 events (our place events):
@Events( startView = RootView.class, historyOnStart = true )
public interface HyperlinkNavigationEventBus extends EventBusWithLookup{

    ...

    @Event( handlers = Page1Presenter.class, historyConverter = PageHistoryConverter.class, name = "page1", navigationEvent = true )
    String goToPage1( String origin );

    @Event( handlers = Page2Presenter.class, historyConverter = PageHistoryConverter.class, name = "page2", navigationEvent = true )
    String goToPage2( String origin );

}
To generate the event token, you need to set the event bus in token generation mode by calling the getTokenGenerator method of the presenter. Then you can call your event method. Since the event bus is in token generation mode, this method will return the event token without actually firing the event.
String tokenPage1 = getTokenGenerator().goToPage1( "You clicked the menu." );
When you set the event bus in token generation mode, it will automatically go back to regular mode whenever you call an event method that can generate token (ie an event method that returns a String). If you don't call one of these methods, your event bus will stay in token generation mode.

Implementing hyperlink navigation for our example


For our example, we want to base our navigation on hyperlink. In order to do so, we need to change our menu.

The first thing we need to change is our menu interface. Instead of having the presenter provide two methods to go to other pages, the view will implement two methods to set each page token.
public interface IMenuView extends IsWidget {

    public interface IMenuPresenter {
        //nothing to implement        
    }
    
    void setPage1Token(String token);
    
    void setPage2Token(String token);

}
The next step is to generate the token for the hyperlink. Our tokens are static so we can generate them only once when we bind the view and the presenter.
@Presenter( view = MenuView.class )
public class MenuPresenter extends BasePresenter<IMenuView, HyperlinkNavigationEventBus> implements IMenuPresenter {
    
    @Override
    public void bind(){
        String tokenPage1 = getTokenGenerator().goToPage1( "You clicked the menu." );
        view.setPage1Token( tokenPage1 );
        
        String tokenPage2 = getTokenGenerator().goToPage2( "You clicked the menu." );
        view.setPage2Token( tokenPage2 );
    }

    ...
}
Finally we have to modify our menu view to use hyperlink widgets.
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui">
    <g:HTMLPanel styleName="menu">
        <table cellpadding="0" cellspacing="0">
            <tr>
                <td>
                    <g:Hyperlink ui:field="page1">Page 1</g:Hyperlink>
                </td>
                <td>
                    <g:Hyperlink ui:field="page2">Page 2</g:Hyperlink>
                </td>
            </tr>
        </table>
    </g:HTMLPanel>
</ui:UiBinder> 
We then need to use these hyperlink widgets to set the page token.
@Singleton
public class MenuView extends ReverseCompositeView<IMenuPresenter> implements IMenuView {

    ...

    @Override
    public void setPage1Token( String token ) {
        page1.setTargetHistoryToken( token );    
    }

    @Override
    public void setPage2Token( String token ) {
        page2.setTargetHistoryToken( token );        
    }        

}
We now have our example with a navigation system based on hyperlink.

What's next?

As you can see, building your application navigation with hyperlink is quite easy thanks to the new feature introduced in Mvp4g-1.3.0. Using hyperlink will greatly improve your user experience but you will have to make sure that the constraints that come with it can work for your project.

In the next post, I will describe another advanced Mvp4g feature to set your own place service.

Monday, June 20, 2011

Navigation Control

One of the main features introduced in Mvp4g-1.3.0/1.3.1 is the navigation feature. This feature allows you to detect when a user leaves a page. The most common use of this feature is when the user is on a page with a form. Before leaving this type of page, you usually want to check if all the data entered by the user is saved. If not, you ask the user to confirm if he wants to leave the page without saving or if he wants to cancel the navigation.

To demonstrate this feature, I'm going to reuse the example developed for the previous tutorial and add a form on page 2. You can download the example here (Project: Mvp4g_Navigation_Control).

Adding a form to our example


This form is pretty simple with only two fields (first and last name) and a save button.

We have the following screen:


Like the rest of the application, we follow the Reverse MVP pattern to develop our form. We then have the following interfaces:
public interface IPage2View extends IsWidget, LazyView {

    public interface IPage2Presenter {
        
        void onSaveClick();

    }
    
    HasValue<String> getFirstName();
    
    HasValue<String> getLastName();
    
    ...
}
When displaying page 2, we update the values displayed on the screen with the local variable value and inversely when clicking on save.
@Presenter( view = Page2View.class )
public class Page2Presenter extends LazyPresenter<IPage2View, NavigationControlEventBus> implements IPage2Presenter {

    private UserBean user;

    ...

    public void onGoToPage2( String origin ) {
        ...
        view.getFirstName().setValue( user.getFirstName() );
        view.getLastName().setValue( user.getLastName() );

        eventBus.setBody( view );
    }

    @Override
    public void onSaveClick() {
        user.setFirstName( view.getFirstName().getValue() );
        user.setLastName( view.getLastName().getValue() );
        view.alert( "User Info saved." );
    }

    ...
}
Now that we have set our form, we need to check that, when the user leaves the page, he doesn't lose any data.

Defining navigation events


The first questions we need to answer are what does it mean to leave a page and when does it occur? In the GWT world, we have a single page application so we can't consider that a user goes to a new page when it loads a new HTML file. Instead the notion of places has been introduced. A place defines where the user is in the application. So leaving a page means going to a new place.

In Mvp4g, you go to a new place by firing an event. Thus the first step to control user navigation is to indicate which event brings the user to a new place.

In order to do this, a new attribute has been added to the @Event annotation, 'navigationEvent'. If set to true, it indicates that when this event is fired, the user is brought to a new place. In our example, we have two place events, goToPlace1 and goToPlace2.
@Events( startView = RootView.class, historyOnStart = true )
public interface NavigationControlEventBus extends EventBusWithLookup{

    ...

    @Event( handlers = Page1Presenter.class, historyConverter = PageHistoryConverter.class, name = "page1", navigationEvent = true )
    void goToPage1( String origin );

    @Event( handlers = Page2Presenter.class, historyConverter = PageHistoryConverter.class, name = "page2", navigationEvent = true )
    void goToPage2( String origin );

}
Whenever one of these events is fired, a control will be performed if a navigation confirmation has been set. This control will also be performed whenever the history token changes.

Creating a navigation confirmation


Now that we have configured our navigation events, we need to create a navigation confirmation to control the navigation. In order to do this, we need to create a class that implements NavigationConfirmationInterface. This interface defines the following method:
void confirm( NavigationEventCommand event );
This method is called whenever a navigation event is fired or if the history token changes. It takes one parameter, a NavigationEventCommand that represents the event. As you may have noticed, this method is asynchronous, which means that it doesn't return a boolean to confirm the event or not. Instead, the method is in charge of confirming the event by calling the fireEvent method of the NavigationEventCommand parameter (if you don't want to confirm the event, you just don't call this method). Why is this method asynchronous? Mainly for two reasons:
  • You may want to call services when confirming your event (to save data before going to a new page for example).
  • You may want to use your own custom confirm popup. Since it is asynchronous, you can easily block any other action until the user answers the question.

To create a NavigationConfirmationInterface, I usually have the presenter implement it directly. This way, I can easily have access to the data and the view to confirm the navigation and I limit the number of classes.

In our example, we want to control that data entered in the form has been saved. In order to do this, we just check if the data displayed in the view matches the data of the local variable. If it doesn't, we display a message to the user using the default browser confirm popup. If data matches or if the user wants to leave the page anyway, we confirm the event by calling the fireEvent method.
@Presenter( view = Page2View.class )
public class Page2Presenter extends LazyPresenter<IPage2View, NavigationControlEventBus> implements IPage2Presenter, NavigationConfirmationInterface {

    ...

    @Override
    public void confirm( NavigationEventCommand event ) {
        boolean sameData = user.getFirstName().equals( view.getFirstName().getValue() ) && user.getLastName().equals( view.getLastName().getValue() );
        if ( sameData || view.confirm( "Data not saved, are you sure you want to leave the page?" ) ) {
            event.fireEvent();
        }
    }
}

Setting a navigation confirmation


Now that we have created our NavigationConfirmationInterface object, we need to tell the application to use it. To do this, we call the eventbus method, setNavigationConfirmation. By default, no confirmation is set so all navigation events will be confirmed and fired. Also, you can only set one NavigationConfirmationInterface at a time for your whole application.

The next question we have is when do we set our NavigationConfirmationInterface? Usually the best time to set it is when it's associated presenter becomes active (ie when this presenter handles the place event). Thus, when handling the place event, we call the setNavigationCommand method.
@Presenter( view = Page2View.class )
public class Page2Presenter extends LazyPresenter<IPage2View, NavigationControlEventBus> implements IPage2Presenter, NavigationConfirmationInterface {
    ...
    public void onGoToPage2( String origin ) {
        //set the presenter to control the navigation when its view is being displayed
        eventBus.setNavigationConfirmation( this );
        ...
    }
    ...
}
Another question you may have now is that if I set a NavigationConfirmationInterface whenever the presenter becomes active, shouldn't I remove it whenever the presenter becomes inactive (ie whenever the user goes to another place)? This is actually done automatically for you. Whenever you confirm an event by calling the fireEvent method of the NavigationEventCommand, this code is executed:
eventBus.setNavigationConfirmation( null );
This code unsets any NavigationConfirmationInterface instance. If for some reason, you don't want this to happen, instead of calling:
event.fireEvent();
you can use:
event.fireEvent(false);

What's next?


We have now setup the Navigation feature. Its first goal is to obviously control users' navigation but it is actually a lot more powerful as you can realize any action you need when the user goes to a new place.

In the next posts, I'm going to continue covering advanced Mvp4g features related to history and navigation by describing the hyperlink token generation feature and the custom place service feature.

Monday, May 16, 2011

Testing a GWT/Mvp4g application in the JVM

I developped a client for a logistics system using Mvp4g and GWT. The system has technically quite complex integration tests, where a J5EE based core application serves multiple WPF thick clients and GWT web clients. I use the same infrastructure to test the overall performance of the system. It might not have been the best decision, but I didn't want to use Selenium or HtmlUnit at that time, so I chose to instantiate my GWT application in the JVM, using mock views.

The post in my blog discusses the solutions that were needed to make in-JVM testing possible.
  • Implementing a simple EventBus using relfection and dynamic proxies.
  • Invoking GWT RPC services from the JVM.
  • Handling GWT i18n in the JVM.
  • Adding performance logging capabilities to RPC invocations.

Friday, April 29, 2011

Grouping multiple presenters

Mvp4g is a great library to manage your large GWT application structure. Especially when using MVP where your Presenters talk to each other using events. Without Mvp4g you can follow plain vanilla GWT but you will need to create and manage a set of Handlers and Events to communicate those presenters. And on top of that, you’ll need to manage the registry and instantiation of those presenter instances.

In an application we recently built we used Mvp4g. We had this special requirement that the screen layout was to be built dynamically. That meant we would need to create presenters on the fly, but not only that, there might be multiple instances of a presenter at any given time - e.g. one screen would have two chart components with different supporting data. The presenter responsibility was the same for both, display a bar chart from the under laying data (a simple multi-column query).

I’ve used a simple concept of sender and receiver for events in groups. These are really simple implementations of one presenter sending events on list box value change and another presenter receiving that event. Look at the sample demo:

Quick running demo with this idea implemented


This blog series is divided in three parts (follow the links for the implementation details):


Sunday, April 24, 2011

History with Mvp4g

Even if you develop an ajax application with GWT (aka one page so one url application), you can still support browser history to improve the user's experience. GWT provides an easy mechanism that lets you store a token in url to simulate url changes. This way your user can navigate through your site thanks to the browser back and next arrows or bookmark different parts of your website. If you're not familiar with this mechanism, you can read the GWT documentation.

Setting up your history is probably the most important decision you will make for your application. You will need to decide when to save your application state and what to save to retrieve this state.

Mvp4g browser history support is based on a place service that can convert any event to a token and can then store it in the url (via the GWT History class). This place service will also listen to any url token changes and convert the token back to an event. Thus you can save your application state whenever you fire an event.

To convert your event to/from a token, the place service uses HistoryConverter. The history converter will be in charge of:
  • saving the necessary information to retrieve the application state when converting an event to a token,
  • retrieve this information and fire event(s) to set the application to the expected state.

In this tutorial, we'll see how to create and set history converter to manage browser history. I will also go over some advanced techniques to reduce the boilerplate code when writing history converters for multiple events.

What to bookmark?


We're going to reuse the example created for the first post. When looking at it, we can define 2 places where the user can be: displaying page1 screen or displaying page2 screen. We go to these places thanks to 2 events, goToPage1 and goToPage2. In the previous article I categorized these events as place events because their role is similar to the concept of Place introduced in GWT 2.1.

Thus we need to store these 2 events in our application. When storing these events, we also need to store the application state. In our case, the state is pretty simple, it's just the string fire with these events.

You can download the code of the example here (Project: Mvp4g_History).

Your first history converter


Creating your history converter

When creating a history converter, you need to implement the HistoryConverter interface and define the following methods:
  • convertFromToken: this method parses the parameters retrieved from the url and fires the appropriate event,
  • handling method of each event it has to convert. In this case, instead of returning void, it returns the event parameters string representation. This string will be added to the token,
  • isCrawable: if this method returns true, Mvp4g adds a “!” in front of the token so that it can be crawled by a search engine.

Now we'll create our history converter for page1.

The first step is to create our Page1HistoryConverter class.
@History
public class Page1HistoryConverter implements HistoryConverter<HistoryEventBus> {

    @Override
    public boolean isCrawlable() {
        return false;
    }

    ...
}
The class has to be annotated with @History. This annotation helps Mvp4g detect the history converter and allows you to set extra parameters (we'll go over them later in this post).
isCrawable method returns false as we don't need the generated token to be crawled by search engine.

The second step is to define the event handling method in the history converter to return the parameters string representation.

But first let's take a closer look at the token generated by Mvp4g. It can be divided in three:
  • event name: by default it's the event method name but you can override this value using the attribute 'name' of @Event.
  • separator: a character to separate the event name from the parameters string representation. By default, it's “?” but it can be overridden.
  • parameters string representation: this is the value returned by the event handling method of your history converter.

For our goToPage1 event, the string representation of the parameters is the parameter itself since we only have one parameter and it's already a string. Our handling method will be quite simple:
public String onGoToPage1(String name){
    return name;
}

The last step is to implement the convertFromToken method. This method has two goals:
  • retrieve information needed to set the application in the expected state. This information can be retrieved from the token, a cookie or the server.
  • fire event(s) to set the expected application state.
Why is the convertFromToken method in charge of firing the event instead of having Mvp4g do it automatically? There are two reasons for this. First, the process to retrieve application state information can be asynchronous if you have to call the server. Then, you may want to fire a different event than the one stored in the token. For example, the event stored in a token requires the user to be logged in. In your history converter, you could either fire one event if the user is logged in or another event to go to the login page if not.

If we take a look at the convertFromToken parameters, we can see that Mvp4g starts parsing the token for you by separating the event name from the parameters string representation. In our example, the converterFromToken method will be simple. The parameters string representation won't have to be parsed since we only have one string parameter for the event. So all we have to do is fire the goToPage1 event with the parameter stored in the token.
public void convertFromToken( String name, String param, HistoryEventBus eventBus ) {
    eventBus.goToPage1(param);
}

Associating your history converter

Now that we have created our history converter, we need to associate it with an event. This is done thanks to the attribute 'historyConverter' of the @Event that annotates your event method:
@Events(...)
public interface HistoryEventBus extends EventBus {
    ...
    @Event( handlers = Page1Presenter.class, historyConverter = PageHistoryConverter.class, name = "page1" )
    void goToPage1( String name );
    ...
}
We also set a name for our event using the attribute 'name'. Instead of having 'goToPage1' in the url, we'll display 'page1'.

Setting the init event

If you try to start your application now, you will get this error message:
You must define a History init event if you use history converters.
Whenever you set history in your application, you need to set up an init event. The init event is the event fired in case the token is empty or invalid.

Before we had the RootPresenter fire the goToPage1 event when the application started, now we want this action to happen only if the token is empty, otherwise the token stored in the history will determine the page to go to. So instead of having the RootPresenter handling the start event, it will handle a new event, the init event.
@Events(...)
public interface HistoryEventBus extends EventBusWithLookup{
    ...
    @InitHistory
    @Event( handlers = RootPresenter.class )
    void init();
    ...
}
@Presenter( view = RootView.class )
public class RootPresenter extends BasePresenter<IRootView, HistoryEventBus> implements IRootPresenter {
    ...
    public void onInit() {
        eventBus.goToPage1( "The application started." );
    }
    ...
}
We can notice that we annotated our init event with @InitHistory. This tells Mvp4g to use this event in case the token is empty or incorrect.

For more information on the init event, you can read this documentation.

History on start

If you start your application now, instead of having "The application started.", no message is displayed.

This occurs because when your application starts, it won't automatically handle the current token stored in the url. Even if you have no token, the init event won't be fired.

To tell Mvp4g to handle the current token stored in the url, you need to set the 'historyOnStart' attribute of @Events to true.
@Events( ..., historyOnStart = true )
public interface HistoryEventBus extends EventBus {
    ...
}

Setting history for goToPage2


Now that we set history for goToPage1, we need to set it for goToPage2. We could repeat the steps described in the previous section and create a history converter for goToPage2:
@History
public class Page2HistoryConverter implements HistoryConverter<HistoryEventBus> {

    public String onGoToPage2( String name ) {
        return name;
    }

    @Override
    public void convertFromToken( String historyName, String param, HistoryEventBus eventBus ) {
        eventBus.goToPage2( param );
    }

    @Override
    public boolean isCrawlable() {
        return false;
    }

}
In this example, it's ok since we only have two place events but in a real application with tens of place events, you will have to create a lot of boilerplate code.

Fortunately Mvp4g comes with features to let you create a history converter that can be reused for multiple place events.

Simplifying convertFromToken method

Our first step will be to create a convertFromToken method that can be used for both events.

The first implementation we could come up with could be this:
@Override
public void convertFromToken( String name, String param, HistoryEventBus eventBus ) {
    if(name.equals('page1')){
        eventBus.goToPage1(param);
    }
    else {
        eventBus.goToPage2(param);
    }
}
As you can imagine, it is not really convenient since you will have to add a new if statement for each event.

To simplify our method, we'll use another feature of Mvp4g, EventBusWithLookup. Thanks to this feature, you can fire an event using its name. All you have to do is have your event bus extend EventBusWithLookup:
@Events( ... )
public interface HistoryEventBus extends EventBusWithLookup {
...
}
This is really convenient for our convertFromToken method since the event name is one of the method parameters. We can then simplify our method to:
@Override
public void convertFromToken( String name, String param, HistoryEventBus eventBus ) {
    eventBus.dispatch( name );
}
The only thing missing is the string parameter that needs to be passed to our events. In both cases, the parameters string representation is passed directly to the event. With the dispatch method providing by the EventBusWithLookup method, you can pass as many parameters as you want. Our convertFromToken method will end up being:
@Override
public void convertFromToken( String name, String param, HistoryEventBus eventBus ) {
    eventBus.dispatch( name, param );
}
So we now have a simple convertFromToken method that can be used for both events.

Simplifying event handling methods

The first implementation we could come up with for this part could be:
public String goToPage1( String name ) {
    return name;
}
public String goToPage2( String name ) {
    return name;
}
These methods execute the same code so once again, we'd like to remove all boilerplate code.

This time we're going to use the attribute 'type' of the @History that annotates the history converter. This attribute defines which method the history converter has to implement to convert the event parameters to a string. Three options are available:
  • DEFAULT: the history converter has to implement the event handling method for each event,
  • NONE: no parameters string representation will be added to the token. The history converter doesn't have to define any method.
  • SIMPLE: this type has to implement one convertToToken method for each group of events that have the same parameters signature. This is the type we want to use here.

The convertToToken is a method that must return a string (the parameters string representation stored in the token). It has the same parameters as the event method plus a first string parameter that will contain the event name.

Both events have the same parameter signature, one string. We then need to define the following convertToToken method:
@History( type = HistoryConverterType.SIMPLE )
public class PageHistoryConverter implements HistoryConverter<HistoryEventBus> {

    public String convertToToken( String eventName, String name ) {
        return name;
    }
    ...
}
The convertToToken method just returns the string passed with the event. We also need to change the value of the attribute 'type' of @History to SIMPLE.

Associating your history converter to goToPage2

We have now created a history converter with a minimum of code that can be used for both events:
@History( type = HistoryConverterType.SIMPLE )
public class PageHistoryConverter implements HistoryConverter<HistoryEventBus> {

    public String convertToToken( String eventName, String name ) {
        return name;
    }

    @Override
    public void convertFromToken( String name, String param, HistoryEventBus eventBus ) {
        eventBus.dispatch( name, param );
    }

    @Override
    public boolean isCrawlable() {
        return false;
    }

}
The last step is to associate it with the events:
@Events( ... )
public interface HistoryEventBus extends EventBusWithLookup{
    ...
    @Event( ..., historyConverter = PageHistoryConverter.class, name = "page1" )
    void goToPage1( String name );

    @Event( ..., historyConverter = PageHistoryConverter.class, name = "page2" )
    void goToPage2( String name );

}
History converter is built as a singleton so even if it is associated with two events, only one converter will be instantiated.

What's next?


We now have setup history for our place events. HistoryConverter is a pretty simple yet powerful feature to set your history. It allows you to set urls without impacting your application by having a light coupling between the history converters and the rest of your application. You can also easily restore your application state by calling any service or firing any event you need in the convertFromToken method.

I hope with this tutorial, you now have a better understanding of how to manage your history. In the next post, I want to go over advanced history features: navigation control to prevent your user from leaving the page by accident, token generation for hyperlink and custom place service.

Tuesday, April 5, 2011

Layout with Mvp4g

The MVP pattern associated with an event bus is definitely the best way to build a fast and robust GWT application but the learning curve and the boiler plate code are important. That's why Mvp4g has been created. It is really easy to learn since it is based on a simple idea: whenever a presenter doesn't know how to do something, it fires an event to ask the rest of the application to do it . It is also simple to use since all you need is a few annotations.

In this tutorial, I will explain how to set your application layout (menu, header, footer) and display/switch screens following the MVP/event bus pattern thanks to Mvp4g. In the second part, I will introduce a technique to write even less code.

This tutorial assumes that you're familiar with GWT and have some notion of MVP (if not, I suggest you to go over the official GWT documentation). You should also read this page to learn how to set up a Mvp4g project.

Part 1: Your First Application


The application developed in this tutorial is pretty simple:


It's made of an header, a footer, a menu and a body. The body can display 2 different elements: page 1 and page 2. For this tutorial, each view will be a simple label, except for the menu that will contain 2 links to page1 and page2. Thus we have the following layout:

You can download the sample code for part 1 here (Project: Mvp4g_Layout_Part1).

Setting up Presenters/Views

First we need to define a presenter/view pair for each element: HeaderPresenter/HeaderView, FooterPresenter/FooterView, MenuPresenter/MenuView, Page1Presenter/Page1View and Page2Presenter/Page2View . We also need to define a presenter/view pair which will contain other presenters' views: RootPresenter/RootView. For our example, this pair should be able to contain an header, a footer, a menu and a body.

In the MVP pattern, the presenter has a reference to the view. For our example, we'll use the Reverse MVP (or View Delegate) approach which means the view also has a reference to the presenter. This approach lets you define presenter with better view abstraction since all DOM event handlers are added by the view. Thus it's easier to cover them with Junit tests and reuse them with different view implementations (you may have another view's implementation for mobile devices for example). You can read this great article for more information on the different MVP approaches.

To reduce the coupling between the presenter and its view, dependency injection will be used. Thus the first step to build our example is to define interfaces for each presenter/view:
public interface IMenuView extends IsWidget {

    public interface IMenuPresenter {
       
    }

}
We'll add methods to these interfaces later on.

Now that we have our interfaces, we need to implement them. We can divide our presenters in to  two categories: the ones with views that will be displayed once the application starts and the ones that won't. In the first category, we find the HeaderPresenter, FooterPresenter, MenuPresenter and RootPresenter. Since we want these presenters to be built right away, they will extend the Mvp4g BasePresenter class.
In the second category, we have Page1Presenter and Page2Presenter. As a best practice, these presenters (and their views) should be built only when they're going to be displayed for the first time. This way, your application will be faster to start. Mvp4g provides an easy way to build a presenter (and its view) when it handles its first event. All you have to do is have your presenter extend Mvp4g LazyPresenter class.

This is what the implementation looks like:
@Presenter( view = MenuView.class )
public class MenuPresenter extends BasePresenter<IMenuView, LayoutEventBus> implements IMenuPresenter {
   ...
}
public class MenuView extends ReverseCompositeView<IMenuPresenter> implements IMenuView {
   ...
}
Finally we need to set the view/presenter implementation used for injection. Thanks to @Presenter, we set the view implementation to inject to the presenter. By having the view implement the ReverseViewInterface, we tell Mvp4g to inject the presenter to the view.

We need the same code for each presenter/view except for Page1Presenter and Page2Presenter that will extend LazyPresenter.

Defining layout events

Now that we have our presenters/views, we need to define how to display them. We saw before views could be displayed at four different locations (header, menu, body, footer). In Mvp4g, each presenter will need to ask the rest of the application to display its views at a specific location thanks to an event. Thus for each location, we'll have an event (that we can categorize as a layout event).

If you're not familiar with how to create events with Mvp4g, you can look at this page. To sum up, you define your event bus with an interface. For each event, you define a method and annotate it with @Event to set your handlers. Each handler has to define the handling method; this method is named on + event's method name and has the same parameters as the event's method name. An implementation of this event bus is created by Mvp4g and injected in to each presenter. To fire an event, you just have to call the event's method.

These are our layout events:
public interface LayoutExampleEventBus extends EventBus {

    /*
     * Layout events
     */
    @Event( handlers = RootPresenter.class )
    void setHeader( IsWidget header );

    @Event( handlers = RootPresenter.class )
    void setMenu( IsWidget menu );

    @Event( handlers = RootPresenter.class )
    void setBody( IsWidget body );

    @Event( handlers = RootPresenter.class )
    void setFooter( IsWidget footer );

}
We can notice events accept an IWidget not a Widget. This is to have a better abstraction of the view. Also if you use the Widget class (or any class based on GWT.create), you'll most likely need to use GWTTestCase for your Junit tests. This is something you need to avoid because regular Junit test are much faster.

The header, footer and menu need to automatically be displayed when the application starts. In order to do this, we configure an event to be fired when the application starts. This event will be handled by their presenters and in response to this event, they will fire a layout event to tell the application to display their views.

@Presenter( view = MenuView.class )
public class MenuPresenter extends BasePresenter<IMenuView, LayoutExampleEventBus> implements IMenuPresenter {

    public void onStart() {
        eventBus.setMenu( view );
    }


}
We annotate the start event with @Start to tell Mvp4g to fire this event when the application starts.

public interface LayoutExampleEventBus extends EventBus {

    @Start
    @Event( handlers = { HeaderPresenter.class, MenuPresenter.class, FooterPresenter.class, RootPresenter.class } )
    void start();

    ...

}
Now that our layout events are fired when the application starts, they need to be handled. Before, we defined a RootPresenter/RootView to display other presenters' view, thus we'll use it to treat these events. For each event, the RootPresenter will just ask its view to display the received view at a particular location.

The RootView then needs to provide a method for each available position:

public interface IRootView extends IsWidget {

   ...
    
    void setHeader( IsWidget header );

    void setMenu( IsWidget menu );

    void setBody( IsWidget body );

    void setFooter( IsWidget footer );

}
public class RootView extends ReverseCompositeView<IRootPresenter> implements IRootView {
        ...
    @UiField
    SimplePanel header, menu, body, footer;

    @Override
    public void setHeader( IsWidget header ) {
        this.header.setWidget( header );
    }

    @Override
    public void setMenu( IsWidget menu ) {
        this.menu.setWidget( menu );
    }

    @Override
    public void setBody( IsWidget body ) {
        this.body.setWidget( body );
    }

    @Override
    public void setFooter( IsWidget footer ) {
        this.footer.setWidget( footer );
    }

}
and when handling a layout event, the RootPresenter just needs to call the associated RootView's method.
@Presenter( view = RootView.class )
public class RootPresenter extends BasePresenter<IRootView, LayoutExampleEventBus> implements IRootPresenter {

    public void onSetHeader( IsWidget header ) {
        view.setHeader( header );
    }

    public void onSetMenu( IsWidget menu ) {
        view.setMenu( menu );
    }

    public void onSetBody( IsWidget body ) {
        view.setBody( body );
    }

    public void onSetFooter( IsWidget footer ) {
        view.setFooter( footer );
    }
}
Now that each static element is displayed inside the RootView, we need one final step: display the RootView inside our application's html body. To do this, we set the RootPresenter as the start presenter:
@Events( startPresenter = RootPresenter.class )
public interface LayoutExampleEventBus extends EventBus {
...
}
then in the GWT entry point, we start our Mvp4g application, retrieve the start view (which is the view of the start presenter) and add it to the RootPanel.
public class LayoutExampleEntryPoint implements EntryPoint {

   public void onModuleLoad() {
        Mvp4gModule module = GWT.create( Mvp4gModule.class );
        module.createAndStartModule();
        RootPanel.get().add( (Widget)module.getStartView() );
    }
}
We now have our static elements displayed. Thanks to the MVP/event bus pattern, we can see that we have a good seperation of concern. Only the RootView is aware of the actual different location. For example, you can decide to change the menu's location (like moving it from the top to the right of the screen) without impacting the rest of the application by modifing only the RootView. Also the RootView is not aware of the actual view displayed at a specific location, so you can then easily change your menu by modifying just one line of code.

Displaying dynamic views

In our application, we have 2 views that are displayed dynamically inside the body location, Page1View and Page2View. The main difference with static views is that instead of having them displayed when the application starts, they will get displayed when another presenter asks for it. With Mvp4g, this presenter will do this thanks to an event that we can categorize as a place event. This place event will have 2 goals:
  • tell the dynamic view's presenter to display its view. This presenter will execute this action by firing a layout event with its view.
  • send any information needed to display the view. In our example, we'll pass a string that will be displayed in the view.
For each dynamic view, we'll need an event.
@Events( startView = RootView.class )
public interface LayoutExampleEventBus extends EventBus {

    ...

    @Event( handlers = Page1Presenter.class )
    void goToPage1( String name );

    @Event( handlers = Page2Presenter.class )
    void goToPage2( String name );

}
This is how the dynamic view's presenter will handle the event.
@Presenter(view = Page1View.class)
public class Page1Presenter extends LazyPresenter<IPage1View, LayoutExampleEventBus> implements IPage1Presenter {

    public void onGoToPage1(String name){
        view.setName( name );
        eventBus.setBody( view );
    }
    
}
Now that we have defined these events, we can use them anywhere in the application. In our example, we need to fire them when you click on one of the menu links. As we said before, we use the Reverse MVP pattern to define our presenter/view. With this pattern, the view is in charge of managing the click handler (and this is easily done thanks to UiBinder) but it can't fire a Mvp4g event directly, it has to go through the presenter. In our example, the presenter provides two methods to fire these events.
public interface IMenuView extends IsWidget {

    public interface IMenuPresenter {
        void goToPage1();
        
        void goToPage2();        
    }

}
@Presenter( view = MenuView.class )
public class MenuPresenter extends BasePresenter<IMenuView, LayoutExampleEventBus> implements IMenuPresenter {

...

    @Override
    public void goToPage1() {
        eventBus.goToPage1( "You clicked the menu" );
    }

    @Override
    public void goToPage2() {
        eventBus.goToPage2( "You clicked the menu." );
    }

}
Our view (built with a uibinder) can then easily call these methods.
public class MenuView extends ReverseCompositeView<IMenuPresenter> implements IMenuView {

    ...    
    
    @UiHandler( "page1" )
    public void onClickPage1(ClickEvent e){
        presenter.goToPage1();
    }
    
    @UiHandler( "page2" )
    public void onClickPage2(ClickEvent e){
        presenter.goToPage2();
    }

}
The last step is to display a dynamic view when the application starts. There are different ways to do this. I chose to have the RootPresenter take care of this when handling the start event.

@Presenter( view = RootView.class )
public class RootPresenter extends BasePresenter<IRootView, LayoutExampleEventBus> implements IRootPresenter {

    ...

    public void onStart() {
        eventBus.goToPage1( "The application started." );
    }

}
This way, you can easily modify the view to display at start by just modifying the event fired by the RootPresenter.

Once again, we can notice the light coupling between the presenter that fires the place event and the presenter that handles it. You can easily switch the view to display thanks to one line of code.

Part 2: Reducing the boilerplate code thanks to GIN

The header, menu, and footer are static elements that won't change. The setHeader, setFooter and setMenu will then only be used once. Since each event requires some lines of code, it is always better if we can prevent them. Fortunately, Mvp4g offers a GIN based technique to remove these events.

You can download the sample code for part 2 here (Project: Mvp4g_Layout_Part2).

The idea is, instead of passing the static views to the RootView via layout events, we will inject them to the RootView thanks to GIN.
In order to do this, we first need to modify the RootView to inject the static views. We add GIN injection and remove all the setter methods.
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:g="urn:import:com.google.gwt.user.client.ui">
    <g:HTMLPanel styleName="root">
        <g:Widget ui:field="header" />
        <g:Widget ui:field="menu" />
        <g:SimplePanel ui:field="body" styleName="body" />
        <g:Widget ui:field="footer" />
    </g:HTMLPanel>
</ui:UiBinder>
public class RootView extends ReverseCompositeView<IRootPresenter> implements IRootView {

    private static RootViewUiBinder uiBinder = GWT.create( RootViewUiBinder.class );

    @UiField( provided = true )
    Widget header, menu, footer;

    @UiField
    SimplePanel body;

    interface RootViewUiBinder extends UiBinder<Widget, RootView> {
    }

    @Inject
    public RootView( HeaderView header, MenuView menu, FooterView footer ) {
        this.header = header;
        this.menu = menu;
        this.footer = footer;
        initWidget( uiBinder.createAndBindUi( this ) );
    }

    @Override
    public void setBody( IsWidget body ) {
        this.body.setWidget( body );
    }
    
}
In the UiBinder, we can define the header, menu, footer as simple widgets since there is no need for it to know the implementation.

We then have to mark HeaderView, MenuView and FooterView as singleton so that the same instance is injected to the presenter and the RootView.

@Singleton
public class MenuView extends ReverseCompositeView<IMenuPresenter> implements IMenuView {
...
}
Finally we have to remove the setHeader, setMenu and setFooter events (and the methods to handle them in RootPresenter) since they are now useless.

Header, menu and footer don't have to handle the start event anymore since they don't need to execute any action when the application starts. However we need to tell Mvp4g to bind these presenters at start for two reasons:
  • Mvp4g builds a presenter right before it has to handle its first event by calling the presenter's bind method. Any presenter with a view to be displayed must then be built. In our sample code, it's not an issue because our bind methods are empty but for example, you will have an error if you extend a lazy presenter. Thanks to a feature introduced by 1.4.0, you can specify presenters that only need to be binded for an event, without handling it,
  • If a presenter doesn't handle any event or isn't binded for any event or isn't the start presenter, then Mvp4g considers it as useless and ignores it to optimize the application.
A presenter with a view injected in to another view should always at least be binded by an event before being displayed.

Thus we'll change our event bus so that header, footer and menu presenters don't handle the event but are just binded by it:
public interface LayoutExampleEventBus extends EventBus {

    @Start
    @Event( bind = { MenuPresenter.class, FooterPresenter.class, HeaderPresenter.class }, handlers = RootPresenter.class )
    void start();
        ...
}
Thus, thanks to this simple technique, you can reduce the layout events to a minimum. You can reuse this technique anytime you have a static nested view.

What's next?


I hope this tutorial helped you have a better understanding of how you can set your layout thanks to events and have a light coupling between each element. There are still many points to cover to build a complex GWT application. In the next tutorial, I will cover how easy it is to add browser history support.