Registering Routes Dynamically
- Configuring User-specific Routes
- Adding Routes on Application Startup
- Getting Registered Routes and Listening for Changes
- Adding Route Aliases for Dynamic Routes
- Dynamic Registration of @Route Annotated Classes
- Example: Adding a New View on User Login
In addition to registering routes and route templates using the @Route
annotation, you can add and remove routes dynamically during runtime.
This is useful when a route should be added or removed based on changed business data or application configuration at startup, for example.
The RouteConfiguration
class allows you to configure routes to a specific scope.
You can configure routes to:
-
All users using the application scope, or
-
Only certain active users using the session scope.
You can access the scope using the forSessionScope
and forApplicationScope
static methods. All components with an @Route
annotation are added to the application scope.
Configuring User-specific Routes
You can add and remove routes for certain users, for example based on their access rights.
Example: Adding two views for currently active users.
RouteConfiguration.forSessionScope().setRoute("admin",
AdminView.class);
// parent layouts can be given as a vargargs parameter
RouteConfiguration.forSessionScope().setRoute("home",
HomeView.class, MainLayout.class);
A route set for the user can override a route with the same path from the application scope. This means that any statically registered @Route
can be overridden for a specific user, if necessary.
The routes in the session scope are only accessible for the current user for as long as the session is valid. When the session is invalidated by the user logging out, the session-scoped routes are no longer available automatically. It is not necessary to specifically remove these routes.
When removing routes, you need to define precisely which route to remove.
Examples:
-
Removing a navigation target (
AdminView.class
) with all possible route aliases and route templates registered to it.RouteConfiguration configuration = RouteConfiguration .forSessionScope(); // No view AdminView will be available configuration.removeRoute(AdminView.class);
-
Removing a path (
"admin"
) which only removes the target mapped to it.// No path "admin" will be available configuration.removeRoute("admin");
For more related information, see:
Note
| Removing a route in the session scope that had previously overridden a route in the application scope, makes the application-scoped route accessible once again. |
Note
|
When dynamically registering a route, any annotations on the classes are ignored, except when the method used contains Annotated , for example setAnnotatedRoute . See Dynamic Registration of @Route Annotated Classes below for more.
|
Adding Routes on Application Startup
You can register routes during application startup using the ServiceInitLister
.
Example: Using ServiceInitLister
to register a route during deployment.
public class ApplicationServiceInitListener
implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
// add view only during development time
if (!event.getSource()
.getDeploymentConfiguration()
.isProductionMode()) {
RouteConfiguration configuration =
RouteConfiguration.forApplicationScope();
configuration.setRoute("crud",
DBCrudView.class);
}
}
}
See VaadinServiceInitListener for more.
Getting Registered Routes and Listening for Changes
When routes are registered dynamically, you may need to update UI components, like navigation menus, based on the added or removed routes.
You can retrieve the registered route templates using the getAvailableRoutes()
method from the RouteConfiguration
. To be notified of route changes, you can register a listener using the addRoutesChangeListener
method.
Note
| You should use the session registry to monitor changes, because it contains all the routes that are available for the current user. |
Example: Getting available routes and registering a routes change listener.
RouteConfiguration configuration = RouteConfiguration
.forSessionScope();
// add all currently available views
configuration.getAvailableRoutes()
.forEach(menu::addMenuItem);
// add and remove menu items when routes are added and
// removed
configuration.addRoutesChangeListener(event -> {
// ignoring any route alias changes
event.getAddedRoutes().stream()
.filter(route -> route instanceof RouteData)
.forEach(menu::addMenuItem);
event.getRemovedRoutes().stream()
.filter(route -> route instanceof RouteData)
.forEach(menu::removeMenuItem);
});
Adding Route Aliases for Dynamic Routes
When adding dynamic routes, the first route for which a navigation target is added is marked as the main route.
The main route url is returned by the getUrl
methods in a RouteConfiguration
.
Any additional registered route is seen as a route alias.
Example: Adding multiple routes as navigation targets in a RouteConfiguration
.
RouteConfiguration configuration =
RouteConfiguration.forSessionScope();
configuration.setRoute("main", MyRoute.class);
configuration.setRoute("info", MyRoute.class);
configuration.setRoute("version", MyRoute.class);
In this scenario, the configuration.getUrl(MyRoute.class)
method returns main
.
Example: Static class definition equivalent of the above route registration example.
@Route("main")
@RouteAlias("info")
@RouteAlias("version")
private class MyRoute extends Div {
}
If the "main"
path is removed and an alias path remains available for use, the main path is updated to the first alias path found in the registry.
Warning
|
Be cautious when adding or removing routes from the ApplicationRouteRegistry , because this impacts every user of the system.
|
Dynamic Registration of @Route Annotated Classes
If you want to map all routes in the same way using the @Route
annotation, you can configure the routes statically, but postpone registration until runtime.
To skip static registration on servlet initialization, add the registerAtStartup = false
parameter to the @Route
annotation.
This prevents the route being registered on startup to the application-scoped registry.
It also makes it easier to use existing parent chains and paths that are modified from the parent.
Example: Using the registerAtStartup
parameter to postpone route registration.
@Route(value = "quarterly-report",
layout = MainLayout.class,
registerAtStartup = false)
@RouteAlias(value = "qr", layout = MainLayout.class)
public class ReportView extends VerticalLayout
implements HasUrlParameter<String> {
// implementation omitted
}
// register the above view during runtime
if (getCurrentUser().hasAccessToReporting()) {
RouteConfiguration.forSessionScope()
.setAnnotatedRoute(ReportView.class);
}
Example: Adding a New View on User Login
This example demonstrates how to add a new view on user login. Two types of users are available: admin users and normal users. After login, we show a different view, depending on the user’s access rights.
The demo application contains:
-
The
LoginPage
class that defines a statically registered route,""
. This route is mapped to the login used for user authentication.@Route("") public class LoginPage extends Div { private TextField login; private PasswordField password; public LoginPage() { login = new TextField("Login"); password = new PasswordField("Password"); Button submit = new Button("Submit", this::handleLogin); add(login, password, submit); } private void handleLogin( ClickEvent<Button> buttonClickEvent) { } }
-
The
MainLayout
class that contains a menu.public class MainLayout extends Div implements RouterLayout { public MainLayout() { // Implementation omitted, but could contain // a menu. } }
-
The
InfoView
class that defines the"info"
route. This route is not statically registered, because it has theregisterAtStartup = false
parameter.@Route(value = "info", layout = MainLayout.class, registerAtStartup = false) public class InfoView extends Div { public InfoView() { add(new Span("This page contains info about " + "the application")); } }
After login, we want to add a new route depending on the access rights of the user. Two targets are available:
-
AdminView
class.public class AdminView extends Div { }
-
UserView
class.public class UserView extends Div { }
In the LoginPage
class, we handle adding to only the user session as follows:
private void handleLogin(
ClickEvent<Button> buttonClickEvent) {
// Validation of credentials is skipped
RouteConfiguration configuration =
RouteConfiguration.forSessionScope();
if ("admin".equals(login.getValue())) {
configuration.setRoute("", AdminView.class,
MainLayout.class);
} else if ("user".equals(login.getValue())) {
configuration.setRoute("", UserView.class,
MainLayout.class);
}
configuration.setAnnotatedRoute(InfoView.class);
UI.getCurrent().getPage().reload();
}
-
A new target for the path
""
is added to the session-scoped route registry. The new target overrides the application-scoped path""
for the user. -
The
InfoView
class is added using thelayout
setup, configured using the@Route
annotation. It is registered to the path"info"
with the sameMainLayout
as the parent layout.
Note
|
Other users on other sessions still get Login for the "" path and cannot access "info" .
|
A3D9523D-8C1C-4F1A-AAEA-D384880B9795