UI State Management

How do people maintain the state of the UI when the App has several routes, for example a Grid with filters and sort orders set by a user along with the queried data. In the past I would mark the view with @UIScope and @Component. I was told that isn’t recommended. This seems like a common use case but I’m not seeing examples or documentation.

For reference this app will only have a few dozen users max and uses Vaadin 24 + Spring Boot.

You don’t need to use @Component on your view becuase routes are managed by Vaadin and dependency injection will also work there.

Scopes are exactly do define where a certain coponent with state is valid.
With UIScope the state of the component is unique per browser tab.

Why did they told you that this isn’t recommended?

From a Vaadin chat session:

“Few words about best practices after I discussed this with the team. We recommend not using @UIScope for any Route / UI component but instead use the scope(s) purely for bean(s) that has the data and then autowire bean(s) to the route.”

They seem to suggest that scope should not be applied to UI components but only to the underling data bean. I can understand this for a massively scaled application but many Vaadin apps do not require this.

Yes that’s also my opinion. I often have the state in the view directly

Do I understand correctly that what you want to achieve here is that some state (e.g. active filters) for a view would be preserved so that the same state is still there if the user navigates to a different view and then back to the original view?

For this case, the easy but somewhat inefficient way is indeed to make the whole view component UI-scoped even though this means that the whole component instance along with all data referenced from it will remain in memory until the UI is closed. Depending on details about the view, this will typically consume somewhere between 100kb and 10mb of server-side memory per user.

If you want to conserve memory, then you can instead create a UI-scoped bean that only contains those state values and then inject that bean into your unscoped view. As an even leaner alternative, you can also store the settings in the user’s browser (e.g. in a cookie) so that no state at all needs to be kept for a longer duration in server-side memory.

Lets use the Vaadin sample app view “Gird With Filters”, the user has filtered the data, set sort orders, and highlighted the row of interest. Now they look at different view. When they return they want to see the exact same state, even the highlighted row. The default behavior is to reset everything.

Could you show a code sample of a recommended way to handle this use case in v24?

1 Like

Yeah that’s the kind of case that I was thinking of as well. As said, one way might indeed be to just mark the whole view component as @UIScope and live with the fact that it will use some additional memory. You have to benchmark the case in your specific application to understand whether it’s a worthwhile trade-off.

The other alternative is to use a separate bean that only contains the state values but nothing else. The drawback here is that it requires some additional code to copy the values back and forth. Here’s a very simple example of the pattern:

@Route(layout = MainLayout.class)
public class MyView extends VerticalLayout {
    @UIScope @Component
    public static class MyViewState {
        private String value = "";
    }
    
    private final MyViewState state;

    public MyView(MyViewState state) {
        this.state = state;

        add(new TextField("State", this.state.value, event -> this.state.value = event.getValue()));
    }
}

Do more complex UI components work with this pattern? For example, highlighting and scrolling to a specific item in a Grid? An example would be helpful for the grid.

grid.select(object)
grid.scrollToIndex(indexObect)

Note: it might require in-memory data provider.

Grid has these methods nowadays:

void scrollToEnd()

Scrolls to the last data row of the grid.

void scrollToIndex(int rowIndex)

Scrolls to the given row index.

void scrollToItem(T item)

Scrolls to the row presenting the given item.

void scrollToStart()

Scrolls to the beginning of the first data row.

void select(T item)

This method is a shorthand that delegates to the currently set selection model.

1 Like

Grid has APIs for doing those things based on an item instance. The simplest approach is thus to directly store the item instance in the view’s state bean. To further reduce memory use, you can alternatively only store the id of the item and then separately fetch the actual item from the database using the stored id in the view constructor.

For the record, I also created How do I preserve some UI state - Vaadin Cookbook based on this example.

1 Like