Event Bus
The main goal of Mvp4g is to allow you to easily set an event bus and events without creating classes and interfaces for each event. Another goal is to easily see which presenters manages each event.
An event is defined by: * its name (or type) * the objects that can be fired with it.
All you have to do to create an event bus and events is to create an EventBus interface and define one method for each event. An implementation of this interface will be automatically generated by the framework and will be injected to each presenter. A singleton pattern is used to create the event bus.
Thus Mvp4g provides an easy and strongly-type way to define an event bus.
Creating an event bus
To create an event bus, you need to: * create an interface that extends com.mvp4g.client.event.EventBus or com.mvp4g.client.event.EventBusWithLookup (see this page to learn the difference) * annotate it with @Events
Annotation @Events has the following attributes: * startPresenter (mandatory) and startPresenterName (optional), see StartPresenter. * historyOnStart (optional, by default false), use only in case of history management, see Mvp4g & Browse history. * module (optional, by default Mvp4gModule), use only in case of multi-module, see Multi-modules feature. * ginModules (optional, see GIN integration).
@Events(startPresenter = MainPresenter.class)
public interface MainEventBus extends EventBus { ... }
Creating an event
To create an event, you need to: * create a method in your event bus interface. This method shouldn't return anything and can have as many parameters as you want. * annotate it with @Event.
``` @Events(startPresenter = MainPresenter.class) public interface MainEventBus extends EventBusWithLookup {
@Event(...)
public void goToCompany();
@Event(...)
public void changeBody(Widget newBody);
...
} ```
@Event annotation has the following attributes: * handlers and handlerNames (see next paragraph) * calledMethod (see next paragraph) * name: Some Mvp4g methods let you manipulate an event thanks to its name. By default the event's name is the event's method's name but you can override it to ease refractoring (see convertFromToken method, see filterEvent method, see dispatch method). * historyConverter & historyConverterName: used only for history management(see Mvp4g & Browse history) * forwardToModules and forwardToParent (see Multi-modules feature) * activate, activateNames, deactivate, deactivateNames (see see Activating/Deactivating presenters) * navigationEvent (see Navigation Event) * passive (see Passive Event) * broadcastTo (see Broadcast Event) * generate (see Event Generation) * bind and bindNames (see Bind Event)
Adding an handler to an event
Only presenters & event handlers can handle an event. To add an handler to an event you just have to add its class to the attribute 'handlers' of the @Event annotation that annotates the method of your event in your event bus interface.
``` @Event(handlers=CompanyListPresenter.class) public void companyDeleted(CompanyBean newBean);
@Event(handlers={CompanyListPresenter.class, CompanyDisplayPresenter.class}) public void companyCreated(CompanyBean newBean); ```
By adding the presenter class, Mvp4g will be able to retrieve the instance of this class and add it to the list of handlers of the event.
You can also add an handler thanks to the presenter name by adding the name to the attribute 'handlerNames' of @Event annotation (not recommended). In this case, you need to verify that a presenter with the given name exists.
If a presenter is added to an event, it must define the handler method associated to the event. By default, the name of this method is: "on" + the name of the event method and the parameters are the same as the event method parameters.
In the following example: ``` @Event(handlers=CompanyListPresenter.class) public void companyDeleted(CompanyBean newBean);
@Event(handlers=CompanyListPresenter.class) public void companyCreated(CompanyBean newBean); ```
The presenter which class is CompanyListPresenter must define the following methods:
public void onCompanyDeleted(CompanyBean newBean){...}
public void onCompanyCreated(CompanyBean newBean){...}
You can change the handler method's name thanks to the attribute 'calledMethod' of @Event annotation.
Firing an event
To fire an event, all you have to do is calling the event's method:
eventBus.companyCreated(new CompanyBean("company"));
- you can filter events and stop them if needed. This mechanism is automatically removed by Mvp4g if no filter is set see Event filtering
- this method has 2 goals:
- check if the presenter is activated. If not, the event won't be forwarded to it.
- if activated and if it's the first event handled by the presenter, it calls the bind method.
You don't have to manage eventbus creation and implementation, the framework will do it for you. It will also inject it automaticaly to all your presenters.
Activating/Deactivating presenters
In your application, you may want a presenter to stop handling events for a certain time. Mvp4g let you do that thanks to the activate/deactivate options of the @Event annotations. When a presenter is deactivated, no event will be forwarded to it even if it can handle them. To deactivate (or activate) an handler when firing an event, you just have to add its class to the attribute 'deactivate' (or 'activate') of the @Event annotation that annotates the method of your event in your event bus interface.
``` @Event(..., activate=CreatePresenter.class, deactivate=EditPresenter.class) public void displayCreatePage();
@Event(..., activate=EditPresenter.class, deactivate=CreatePresenter.class) public void displayEditPage();
@Event(handlers={EditPresenter.class, CreatePresenter.class}) public void valueChosen(String value); ```
In this example, when you display the create page, the 'valueChosen' event will only be forwarded to the create presenter. At the opposite, when you display the edit page, the 'valueChosen' event will only be forwarded to the edit presenter
You can also deactivate (or activate) a presenter thanks to the presenter name by adding the name to the attribute 'deactivateNames' (or 'activateNames') of @Event annotation (not recommended). In this case, you need to verify that a presenter with the given name exists.
A presenter can also deactivate itself by calling its setActivated method.
Start Presenter & Start Event
Start Presenter
Each module needs to define a start presenter. The start presenter is the presenter which view is added to the RootPanel or RootLayoutPanel when the application starts.
To define a start presenter, you need to specify its class thanks to the attribute 'startPresenter' of the @Events annotation that annotates your event bus interface. Mvp4g will automaticaly find the instance of the presenter if it is annotated with @Presenter or @EventHandler.
In addition to set the class, you can also specify its name thanks to the attribute 'startPresenterName' of the @Events annotation.
In case of a child module that is not displayed thanks to the auto-display feature, you may not need a start presenter. You can then tell the framework that there is no start presenter by setting the 'startPresenter' attribute of @Events to 'NoStartPresenter' (a root module must have a start presenter).
Start Event
When your application starts, you may want to automatically fire an event so that actions needed at first can occur. To define a start event, you need to annotate it with @Start. You can have only one start event by module and no object can be fired with the start event.
``` @Events(...) public interface OneEventBus extends EventBus {
@Start
@Event(...)
void start();
} ```
Logs
Mvp4g integrates a log feature that let you trace the events fired on the event bus and see who handles each event.
To activate the log feature, you need to annotate your event bus class with @Debug:
``` @Events(...) @Debug public interface MainEventBus extends EventBusWithLookup {
@Event(handlers={EditPresenter.class, CreatePresenter.class)
public void displayName(String name);
} ```
@Debug annotation has the following attributes: * logLevel (optional, default:SIMPLE): define the level of log: SIMPLE (only names of fired events will be logged), DETAILED (names of fired events and names of handlers will be logged) * logger (optional, default:DefaultMvp4gLogger): define the class of the logger to use
The following traces will be logged when this code is executed:
eventBus.displayName("name");
* SIMPLE: Module:
Mvp4gModule || event: displayName || param(s): name
- DETAILED:
Module: Mvp4gModule || event: displayName || param(s): name com.mvp4g.example.client.presenter.EditPresenter@4ec6ca37 handles displayName com.mvp4g.example.client.presenter.CreatePresenter@1ed4ca37 handles displayName
Mvp4g logger
By default, Mvp4g will use the DefaultMvp4gLogger. This logger uses GWT.log to display the logs. You can easily use the logger of your choice (like gwt-log for example). All you have to is to create a logger that implements Mvp4gLogger.
``` public class CustomLogger implements Mvp4gLogger {
public void log( String message, int depth ) {
String indent = "";
for ( int i = 0; i < depth; ++i )
indent += " ";
//really annoying log :)
Window.alert( indent + "CustomLogger: " + message );
}
} ```
and then tell Mvp4g to use this logger thanks to @Debug annotation:
@Events(...)
@Debug(logger=CustomLogger.class)
public interface MainEventBus extends EventBusWithLookup {...}
EventBus with lookup
Similar to GWT internationalization system with Constants and ConstantsWithLookup, Mvp4g let you define an event bus with lookup, which means that you can fire an event thanks to its name. In order to do so, an event bus with lookup has an extra method: dispatch.
Instead of:
eventBus.login("myUserName");
you can use:
eventBus.dispatch("login","myUserName");
Mvp4g will automaticaly find the right method to call on your event bus.
To create an event bus with lookup, all you have to do is implementing EventBusWithLookup interface instead of EventBus interface.
However this functionality should be used only when needed and with caution for two reasons: * Mvp4g can't verify at compile time if an event with the given name exists. You will see the error only when you execute your code. * It is not type safe. If you decide to fire an event with object(s) not managed by the event, Mvp4g won't detect it at compile time and an error will occur when you execute your code.
``` //let's say this call is correct eventBus.dispatch("login","myUserName");
//call to an event that doesn't exist because of a typo error eventBus.dispatch("lgin","myUserName");
//call with an object which type is incorrect eventBus.dispatch("login",new User("myUserName")); ```
Event Filtering
Mvp4g allows you to filter events in order to stop an event before it is forwarded to handlers.
Creating Filters
To create a filter, you need to: 1. implement EventFilter
<E>
-E: type of your event bus interface.
- override filterEvent method: this method will allow to stop an event or not. If filterEvent method returns false, then the event is stopped, otherwise, it is forwarded to the handlers.
Adding Filters
Once you have created a filter, you need to tell Mvp4g to use it. This will be done thanks to the @Filters that annotates your event bus interface.
@Events( ... )
@Filters( filterClasses = OneEventFilter.class )
public interface OneEventBus extends EventBus {...}
Annotation @Filters has the following attributes: * filterClasses: set one or several filters to use. An instance will be created for each class specified. * afterHistory (optional, by default false): set if events should be filtered before or after browser history convertion. If this attribute is set to true (ie events are filtered after history convertion), even if the event is stopped, a token will still be stored in browser history for this event. * filterStart (optional, by default true): if false, the event defined as the start event won't be filtered when fired at the application start. * filterForward (optional, by default true): if false, the event defined as the forward event won't be filtered when fired when an event is forwarded to a child module. * forceFilters (optional, by default false): if no filter is set thanks to the @Filters annotation, Mvp4g automatically optimizes the code and remove the filter mechanism from the generated code. This means that if you try to add filters dynamically (see below), it won't work. In order to force Mvp4g to keep the filter mechanism, you can set this attribute to true.
You can also dynamically add or delete filters thanks to the addEventFilter and removeEventFilter methods of the event bus.
OneEventFilter filter = new OneEventFilter();
eventBus.addEventFilter(filter);
eventBus.removeEventFilter(filter);
Temporarily disable filtering
In some cases, you may need to temporarily stop events filtering. In order to do so, Mvp4g provides two methods using the event bus, setFilteringEnabled and setFilteringEnabledForNextOne. The first method allows you to enable/disable filtering for all events of the event bus. The second one allows you enable/disable filtering for the next fired event (other events fired while handling this event won't be affected by the call of this method).
In this example, none of the events fired will be filtered:
eventBus.setFilteringEnabled(false);
eventBus.selectCompanyMenu();
In this example, only the selectCompanyMenu event won't be filtered:
eventBus.setFilteringEnabledForNextOne(false);
eventBus.selectCompanyMenu();
In this example, selectCompany event and all the events that are fired by handlers of selectCompany event won't be filtered:
eventBus.setFilteringEnabled(false);
eventBus.selectCompanyMenu();
eventBus.setFilteringEnabled(true);
Navigation Event
Navigation event represents events that, when fired, usually lead to the load of a new screen (but it's not mandatory), like if the user navigates to a new page. A special control can be added to stop this type of events before it is forwarded to the handlers in order to prevent the user to switch page. A common use case of this control is to prevent user to loose data when leaving a page.
Defining an event as a Navigation Event
To define an event as a navigation event, you just need to set the navigationEvent attribute of @Event to true (by default it is set to false):
@Event(..., navigationEvent = true)
void goToPage1();
Creating a control object for navigation events
To create a control object, you need to: * implement NavigationConfirmationInterface * define the confirm method.
When an event needs to be control, the confirm method is called with one parameter, a NavigationEventCommand that represents the event to control. To confirm an event, you just have to call the NavigationEventCommand's method fireEvent. To stop an event, just don't call this method.
``` public class Presenter extends ... implements NavigationConfirmationInterface {
public void confirm(NavigationEventCommand event){
//pseudo method to verify if the view has changed
if(isViewModified(){
//Window shouldn't be used inside a presenter
//this is just to give a simple example
if(Window.confirm("Are you sure you want to leave?")){
event.fireEvent();
}
}
else{
event.fireEvent();
}
}
} ```
Navigation control is asynchron. The main advantage of this solution is that you can use your own widget to ask for user's confirmation
``` public class Presenter extends ... implements NavigationConfirmationInterface {
public void confirm(NavigationEventCommand event){
view.getConfirmWidget().setYesClickHandler(new ClickHandler(){
public void onClick(ClickEvent event){
event.fireEvent();
}
});
}
} ```
By default, calling the fireEvent method will automatically remove the navigation control in order to prevent other navigation events to be controlled. If a user confirms that he wants to leave a screen, then no control should be done until a new screen is displayed. When displaying the new screen, you should set a new control.
However if for any reason you don't want to remove the control, you can just call the fireEvent method with a false parameter:
event.fireEvent(false);
Setting a control for navigation events
Whenever you display a screen that needs user's confirmation before leaving it, you need to set a NavigationConfirmationInterface instance. In order to do this, you need to call the eventBus's method setNavigationConfirmation with the NavigationConfirmationInterface instance to set
eventBus.setNavigationConfirmation(navigationConfirmationInstance);
If you need to remove the NavigationConfirmationInterface instance, just call the setNavigationConfirmation method with a null parameter:
eventBus.setNavigationConfirmation(null);
You can set only one NavigationConfirmationInterface instance for the whole application. This instance will be used to control any navigation events even fired by other Mvp4g modules.
Passive Event
Unlike a regular event, a passive event won't automatically build presenters or load child modules that handles it. Thus a passive event will be forwarded only to presenters/child modules that have already been build/loaded.
To set an event as passive, you just need to set the passive attribute of @Event to true (by default it is set to false):
@Event(..., passive = true)
void sessionExpired();
Broadcast Event
You can easily forward an event to a lot of handlers or modules thanks to the broadcast feature. You can specify a broadcast interface for an event and any handler or module that implements this interface will receive it if: * the handler is compatibable with the Mvp4g module defining the event (ie if the Mvp4g module's event bus can be injected into the handler). Otherwise the handler will be ignored. * the module is a child module, a sibling module or a parent module of the Mvp4g module defining the event.
This interface should be as simple as possible and doesn't need any method.
public interface OneBroadcastInterface {}
@Event(...,broadcastTo=OneBroadcastInterface.class)
void oneEventToBroadcast();
Bind Event
If you need a presenter to be binded when an event is fired but actually don't need this presenter to handle this event (which is often the case for Nested View).
@Event( ..., bind = { FirstNestedViewPresenter.class, SecondNestedViewPresenter.class } )
public void start();
By adding a presenter to bind attribute, Mvp4g will bind this presenter the first time this event is fired.
You can also add a bind thanks to the presenter name by adding the name to the attribute 'bindNames' of @Event annotation (not recommended). In this case, you need to verify that a presenter with the given name exists.
You can't use the same presenter in handler and bind annotation for one event because a presenter set as an handler will be already automatically binded when the event is fired. Also, Mvp4g prohibits using bind attribute for passive events. Passive event, by definition, don't bind handlers (only handlers already binded will react to the passive event).
You don't need to provide any methods in Presenter when you're using bind annotation. It will call 'bind' method of your presenter automatically.