Blog

Try Vaadin 23.2 Beta for Improvements in View-to-View Navigation

By  
Matti Tahvonen
Matti Tahvonen
·
On Sep 1, 2022 4:11:05 PM
·
In Product

Vaadin 23.2 is already available as a release candidate. Along with some UI component enhancements, it comes with a couple of noticeable improvements to the core framework, Vaadin Flow. The automated front-end bundling done by Vaadin becomes much faster as we swap from webpack to Vite. While that change makes developing with Vaadin feel smoother, we’ve also made improvements to the routing framework that can make your code look better and be easier to maintain.

What is Routing and why should you use it at all?

Routing in Vaadin, and many other web frameworks, means the mechanisms to achieve “deep linking”. People are used to sharing links from their browser with friends and colleagues. Using routing, we can maintain just enough view state in the URL (including the view name and identifier of an entity shown in the UI) so that the framework can re-create the same UI for your colleague who you shared the link with.

Of course, you still have the option to create single-page web applications without deep linking or just implement deep linking to a certain level. Leaving it out makes certain things easier, but you can lose important functionality that your users expect from a web application. As a developer, you can choose if, or to what extent, your app will support deep linking. Personally, I have a habit of always using the routing API for top level navigation, as that is trivial to use, but then consider further support for deep linking based on app characteristics.

Meet the Java native view-to-view communication

When the routing framework was completely re-designed for Vaadin 10, all communication between different views was solely designed to happen via various URL parameters. At that time, we had plans to make Vaadin a true general-purpose web framework, also for static websites. As opposed to web apps, deep linking is a must-have feature for websites; thus, we were pretty purists in the design.

As the framework didn’t provide any natural way to interact with other views, we started to see a trend of various hacks, for example, using dependency injection for direct view-to-view communication. Also, some developers used the somewhat cumbersome URL parameter passing (no proper typing, only raw data types supported), even though a proper deep linking was not really needed (or even supported in other parts of the UI).

In Vaadin 23.2, most of the UI.navigate methods used in the routing framework to switch between the views now return the actual instance of the target view. This opens the easiest, fastest, and safest method to communicate between your views: the pure Java API.

Consider an example where navigating from a view listing complex objects to a view that edits a single entity. Previously you needed to think of a way to somehow serialize the state of the edited object to the URL, pass that to the navigate method, and then in the editor view, re-create the same object based on the details encoded to the URL, potentially causing multiple expensive backend calls. Now, you can pass the (potentially complex) Java object straight through a Java API to the next view.

new Button("Edit " + user.getName(), event -> {
    ui.navigate(UserEditor.class)
            .ifPresent(editor -> editor.editUser(user));
})

Code example: In Vaadin 23.2 you can directly call a method in the target view. Previously you would have needed to create an identifier for the user object, pass that in the navigate method, and do the reverse in the UsersEditor class.

If you are wondering about the ifPresent part of the code example above, we are actually returning an Optional of the target view. The routing functionality in Vaadin Flow allows developers to cancel navigation in certain cases and we want to avoid accidental NPEs.

Do I lose the deep linking if using the new mechanism?

Not necessarily. Using the pure Java API certainly makes the view navigation look better in your codebase, and you may save a couple of expensive database queries, but by default, the router now only updates the view name in the URL. If deep linking is required for your view, you can still take advantage of the new model by moving the responsibility of keeping your URL up-to-date to your target view. In certain views, even today, user interactions in the view itself should also update the URL, so there is a high chance that you can now write all URL maintenance logic into one logical place; to the view itself.

In a code snippet below, we update the deep linking URL in the target view, even though the view receives the parameter through the native Java API.

/**
 * This method can be called directly by other views.
 *
 * @param user the User instance to edit
 */
public void editUser(User user) {
    // do actual UI changes
    createFormForUser(user);
    // maintain a complete url in the browser
    updateUrlParameters(user);
}

private void updateUrlParameters(User o) {
    String deepLinkingUrl = RouteConfiguration.forSessionScope()
            .getUrl(getClass(), o.getId());
    // Assign the full deep linking URL directly using
    // History object: changes the URL in the browser,
    // but does not reload the page
    getUI().get().getPage().getHistory()
            .replaceState(null, deepLinkingUrl);
}

Code example: updating the URL parameters within a view.

A bunch of other navigation-related improvements

While working on the routing, we also improved the existing functionality. It is now possible to pass both “query parameters” (the part after the question mark in the URL) and “route parameters” at once. We also created a helper to generate trivial key-value query parameters. We also exposed a method to get a reference to the Router object using any component.

When building examples and tests, we also noticed that it is not always the new target view you want to interact with. Sometimes you might want to use an API from your parent view/layout; for example, collapsing navigation for certain “full-screen views” or placing additional content in a slot in the main layout. For this purpose, we introduced a helper from the Component class to search for an ancestor from the component tree. For example, closing the drawer from a MainLayout in a start.vaadin.com generated project can be accomplished like this:

findAncestor(MainLayout.class).setDrawerOpened(false);

Try Vaadin 23.2 today!

As of today, the latest cut of the Vaadin 23.2 branch is 23.2.0.rc1. The final release is out on September 7th, so we’d appreciate you trying the new features in your apps and reporting all findings we might have missed!

Matti Tahvonen
Matti Tahvonen
Matti Tahvonen has a long history in Vaadin R&D: developing the core framework from the dark ages of pure JS client side to the GWT era and creating number of official and unofficial Vaadin add-ons. His current responsibility is to keep you up to date with latest and greatest Vaadin related technologies. You can follow him on Twitter – @MattiTahvonen
Other posts by Matti Tahvonen