#!/home #!/admin/settings #!/admin/users #!/admin/users/id/4711/showHistory/startDate/2017-01-01/endDate/2017-01-31 #!/admin/users/id/4711/profile/activeTabsheet/address
This Vaadin add-on enhances the standard Vaadin Navigator component with a sophisticated interpreter for URI fragments. Using this add-on, you can design the URI fragments used by your application in an arbitrarily complex hierarchy. Furthermore, your URI fragments can contain any number of parameter values which will be interpreted and converted automatically so that these values can be handled in a type-safe way.
For example, it is possible to use URI fragments like in the following example:
#!/home #!/admin/settings #!/admin/users #!/admin/users/id/4711/showHistory/startDate/2017-01-01/endDate/2017-01-31 #!/admin/users/id/4711/profile/activeTabsheet/address
As you can see, these URI fragments form a hierarchy where each individual path element can have an arbitrary number of parameters.
The basis for this feature is established by the library uri-fragment-routing. This library is responsible for the URI fragment interpretation process, the extraction and conversion of parameter values and the management of the URI fragment hierarchy.
This Vaadin add-on simply consists of a single wrapper class around a common Vaadin Navigator
and the boilerplate code to make the URI fragment interpretation process of library uri-fragment-routing
available for Vaadin applications.
This library uses a different approach for interpreting URI fragments than the standard Vaadin Navigator
. While the Navigator
tries to extract a view name from the URI fragment, the uri-fragment-routing
library interprets a URI fragment completely and tries to resolve it into an action command class. If such a class could be found for a given URI fragment, it is instantiated and executed. Thus, this library uses the Command Design Pattern, while the hierarchical URI fragments are interpreted using the Chain of Responsibility Design Pattern.
For details about the correct usage of the library uri-fragment-routing
, please consult the documentation provided on the library’s project page.
To include the add-on in your project, you only need to add a new dependency on the add-on in your build configuration. For Maven, this would be:
<dependency> <groupId>org.vaadin.addons.uriactions</groupId> <artifactId>uri-fragment-actions-for-vaadin</artifactId> <version>1.0.0</version> </dependency>
Adapt the version accordingly.
The add-on consists only of two classes one of which is the important one: UriFragmentActionNavigatorWrapper
. This class is a wrapper around a standard Vaadin Navigator. This wrapper enhances the navigator with additional functionality, namely the option to resolve parameterized URI fragments to action command objects. The navigator wrapper determines the action command which is responsible for a given URI fragment, instantiates an object of this action command, and executes this subsequently.
Class UriFragmentActionNavigatorWrapper
offers the same constructors as class Navigator
. This means that you can add a custom ViewDisplay
or a ComponentContainer
to the navigator wrapper as you can do with a common navigator.
However, the navigator wrapper additionally offers the option to pass a UriActionMapperTree
object into it which is the basis for resolving parameterized URI fragments into action command objects. If you provide an action mapper tree, then this tree has precedence in the URI fragment interpretation process. That is, it is first attempted to resolve a URI fragment using the action mapper tree. Only when a fragment could not be resolved with the action mapper tree, it will be handled by the built-in mechanisms of class Navigator
.
By that, you can choose to handle simple cases with the standard Vaadin mechanism (e. g. using a SingleComponentContainerViewDisplay
or a ComponentContainerViewDisplay
) and complex cases with a UriActionMapperTree
.
To do that, you can add custom view classes or ViewProvider
objects to the wrapped navigator. You can obtain the reference of the wrapped navigator with method getNavigator()
. The View
and ViewProvider
objects that you add to the wrapped navigator will be used by the navigator in the usual way. Note that you don’t need to add any specific views or view providers to the wrapped navigator if you want URI fragments to be handled only by the URI action mapper tree.
For detailed information about the individual features of class UriActionMapperTree
and how to create and configure an instance of this class, please consult the uri-fragment-routing
library’s user manual.
A UriActionMapperTree
is thread-safe and can be shared by all UI
objects. It is therefore important that the action mapper tree object is created with application-scope (e. g. as a static singleton or using an application-scoped service). It should be avoided to create this instance in the init()
-method of your application’s UI
class. This would unnecessarily waste a lot of memory. A consequence of this is that this application-scoped URI action mapper tree needs to be configured in such a way that it can handle any valid URI fragment of an application. Access control needs to be implemented either on the level of the action commands or on the service layer.
When you have created a UriActionMapperTree
object, you can pass it to the navigator wrapper with setUriActionMapperTree()
. We will look at a simple example where we want to build an action mapper tree which is able to handle the following URI fragments:
#!user #!user/profile #!admin #!admin/users #!admin/groups #!settings
This can be achieved with the following code. Note that in this simple example, the URI action mapper tree is stored in a static variable.
public class MyUI extends UI {
private final static UriActionMapperTree MAPPER_TREE;
static {
MAPPER_TREE = UriActionMapperTree.create().buildMapperTree()
.mapSubtree("user").onActionFactory(() -> new NavigateToActionCommand("userPage"))
.onSubtree()
.map("profile").onActionFactory(() -> new NavigateToActionCommand("profilePage")).finishMapper()
.finishMapper()
.mapSubtree("admin").onActionFactory(() -> new NavigateToActionCommand("adminPage"))
.onSubtree()
.map("users").onActionFactory(() -> new NavigateToActionCommand("userAdminPage")).finishMapper()
.map("groups").onActionFactory(() -> new NavigateToActionCommand("groupAdminPage")).finishMapper()
.finishMapper()
.map("settings").onActionFactory(() -> new NavigateToActionCommand("settingsPage")).finishMapper()
.build();
}
@Override
protected void init(VaadinRequest request) {
UriFragmentActionNavigatorWrapper navigatorWrapper = new UriFragmentActionNavigatorWrapper(this);
navigatorWrapper.setUriActionMapperTree(MAPPER_TREE);
}
}
As you can see, action command objects are created by a functional factory interface. These factories are defined as lambda expressions in this example.
You only need to create one instance per UI
object of the navigator wrapper and pass this instance the application-scoped action mapper tree object. The action command objects (NavigateToActionCommand
) created by the factory objects defined in the mapper tree will take care of displaying the respective pages when executed.
As described in the uri-fragment-routing
library’s user manual, a routing context object is an object which contains data or references to services needed by the URI action commands when they are executed. Such a context object may, for instance, contain a reference to the current UI
object, to an event bus, or to the necessary back end services. You can define one such routing context object per navigator wrapper instance which boils down to one routing context object per UI
instance. You set this context object during the initialization of the navigator wrapper:
@Override
protected void init(VaadinRequest request) {
Eventbus eventBus = new Eventbus();
UriFragmentActionNavigatorWrapper navigatorWrapper = new UriFragmentActionNavigatorWrapper(this);
navigatorWrapper.setUriActionMapperTree(MAPPER_TREE);
MyRoutingContext routingContext = new MyRoutingContext(this, eventBus);
navigatorWrapper.setRoutingContext(routingContext);
}
The navigator wrapper now makes sure that this context object will be passed to any action command object which requires it.
When a URI fragment is successfully interpreted by the URI action mapper tree, an action command object will be executed at the end of this process. This action command is created, executed and discarded by the navigator wrapper internally so that under normal circumstances you will not get in touch with this object. However, if you need access to the action command object which has just been executed, you can obtain this reference using a ViewChangeListener
. You can register a ViewChangeListener
on the wrapped navigator object in order to get a reference to the internally used ActionExecutionView
which contains the currently executed action command object.
Class ActionExecutionView
provides a method getUriActionCommand()
which returns this action command object. The following examples outlines how to access this object.
navigatorWrapper.getNavigator().addViewChangeListener(new ViewChangeListener() {
@Override
public boolean beforeViewChange(final ViewChangeEvent event) {
return true;
}
@Override
public void afterViewChange(final ViewChangeEvent event) {
if (event.getNewView() instanceof ActionExecutionView) {
final ActionExecutionView view = (ActionExecutionView) event.getNewView();
final UriActionCommand uriActionCommand = view.getUriActionCommand();
// further process the action command ...
}
}
});