Saturday, December 30, 2017
new Gitter room
All you need to use it, is a GitHub account!
Tuesday, September 13, 2011
A Complex-Layouting Library over mvp4g
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 place to take the jar from
- Running Demo
- Documentation
- Source code of the library
- Source code of the demo
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
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
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
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:
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
- 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:
This blog series is divided in three parts (follow the links for the implementation details):