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.
“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.
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.
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 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.