Are there any examples of Vaadin/Spring-Boot that go beyond “Hello World” simplicity?
I prefer Spring @Configuration classes so that spring doesn’t waste time scanning every package on startup. Vaadin does not support @ComponentScan and we have to use @EnableVaadin. Fine. But is it possible to turn off scanning entirely and provide everything via a org.springframework.context.annotation.@Configuration spring config class? I believe not.
Some of the Vaadin class annotations are configured as Target TYPE only, so we cannot place them on a @Bean method in a @Configuration class. Is there a technical reason for this?
The @PermitAll annotation is allowed on a method but then is ignored. Seems like a bug?
The documentation on @Component/@SpringComponent@Scope/@VaadinSessionScope still leaves plenty of room for developers to do things catastrophically wrong. For a long time, I used @VaadinSessionScope on my UI Component classes and suffered StateTree exceptions without knowing how to fix them. I still don’t know how to fix them without abandoning the use of “session scope components”. See SessionScope vs. VaadinSessionScope - Which one should you use? | Vaadin comment section.
@PreserveOnRefresh seems to be required when navigating using the History urls. That reloads pages. Without @PreserveOnRefresh, I get StateTree exceptions.
Even when I use @PreserveOnRefresh and @Scope(“prototype”) on my MainLayout class, using the browser back button to take me all the way out to the Login page will throw the StateTree exception. I have no idea how to fix this.
Is there any benefit of a spring @Configuration class for UI components, given that any methods have to be duplicated with the @EnableVaadin? For example, my code currently looks like:
@EnableVaadin({
"com.mycompany.app",
})
public class VaadinApplication implements AppShellConfigurator {
}
@Configuration
public class VaadinConfig {
@Bean @Lazy @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MainLayout MainLayout() {
return new MainLayout();
}
}
@PreserveOnRefresh
public class MainLayout{
}
I just want an app where the server-side does all the normal navigation without reloading pages, updates the url and history as it navigates, and reuses the main UI instances in what is essentially VaadinSessionScope. (I rely on these instances being in the VaadinSession for navigation.) But also that user browser navigation is supported (forward/back/history/urls). I can’t figure it out.
Or am I making a fatal mistake trying to use UI components as user-session-scoped? According to this comment (Reusing component instances - #8 by Tatu2), I probably am. (It suggests copying all UI component state to a VaadinSessionScope bean during AfterNavigation, then importing it during BeforeNavigation.) But this is going to be slow, as it is an expensive operation to configure and populate the UI.
Has anyone tried something as radical as detaching the Component from the UI during AfterNavigation, storing that in the session, then doing a parent.replaceComponent() operation during BeforeNavigation?
I will let the others answer your other questions - but this can be said as simple as: Never. And I repeat. Never use session scoped components. It’s impossible to work. A Vaadin Session like a HTTP Session is stored for a whole User’s Browser - meaning each tab shares the components. You see where this is going, right? It would be like opening google on a second tab and you stealing the search input field from tab one and placing it on the second.
If you really want components to be Beans, you must use Prototype scope or Vaadin’s custom UIScope or RouteScope.
That is a Jakarta annotation (jakarta.annotation.security.PermitAll) so Vaadin doesn’t control it. If you want to apply method level security, it’s up to you to implement it.
No, I usually do not see need for that. If I want to preserve some data populated into my component, I use suitable scope business bean, e.g. session scoped where I have that data. I.e. I am not storing the entire component, just the data. (I can then have my component prototype or RouteScoped if it is not @Route), which ensures that I am not attempting re-use the component in other parents, which might cause problems. I am just autowiring the data bean and reading the data from there, e.g. afterNavigation.
You maybe wrong here. Instantiating server side Java object is fast, unless you need to fetch data from the backend. Also typical UI updates are quite fast. When Vaadin client gets data from the server, it will update the page content according to information it receives. This by complextity does not differ much from e.g. React, which does the same thing each time it observes state changes.
What do you try to achieve by doing things in those non-typical ways?
Sure, there’s some small overhead to scanning for Vaadin dependencies in the way it does. But that’s only during application startup which is typically not a hotspot for optimization. You could try limiting the scanning using vaadin.allowed-packages to a single “configuration” class that references everything else using the Vaadin @Uses annotation. But how much time would you really save by doing that?
The same also goes for trying to cache components in the session. As already pointed out, the problem with that is that the same session is shared between multiple tabs, or even page reloads within the same tab unless you use the not-yet-released partialMatch mode in @PreserveOnRefresh. But the key question here is also how much you even save by doing that? My guess is that the majority of all gains could be achieved by caching data rather than component instances since the overhead of creating the component instances is typically small in comparison to the data loading and also overshadowed by the work that anyways happens when an existing component instance is re-attached. Have you measured the impact?
My guess is that the StateTree exception is mainly because you’re putting a Vaadin component in the session. That’s the first thing to change.
PreserveOnRefresh is only here to preserve the view on refresh. If you’re adding this to hide an exception it doesn’t fix the issue and something is wrong. (see the previous paragraph)
I don’t think PreserveOnRefresh is a good annotation, most of the application shouldn’t deal with an issue to have a view recreated if the user is clicking on refresh.
The @PermitAll annotation is allowed on a method but then is ignored. Seems like a bug?
Also you need to manually check it. For example you can allow a save action only for ADMIN. Click on the save button will throw an exception but I think the proper way of doing this is to hide or disable the save button.
One of the first “advanced” things I did when evaluating Vaadin was implement a Button that “tore off” a Search Layout from the main window and moved it to a separate browser tab. Powerful feature to demo.
But I fear the fact that the user can F5 and navigate History is a fatal blow to certain patterns. I see this now. Thank you.
Well that is interesting. Moving @PermitAll from Configuration class to actual class changed how the app functioned. A side-issue to pursue. Thank you.
What do you try to achieve by doing things in those non-typical ways?
How are we supposed to know these are “non-typical”? Documentation usually demonstrates just the basic steps. The rest is assumptions in our heads and previous experience.
A favorite quote: “It is not what I don’t know that gets me into trouble. It is what I know that ain’t so.”
Sure, there’s some small overhead to scanning for Vaadin dependencies in the way it does. But that’s only during application startup which is typically not a hotspot for optimization.
Slow Spring startup is one of the biggest complaints. Turning off full package scan and moving to a single Configuration class is a standard approach. It also lets a developer look in one place rather than having to search code by keywords.
You could try limiting the scanning using vaadin.allowed-packages to a single “configuration” class that references everything else using the Vaadin @Uses annotation.
I’d like to do just that! However, I would prefer a java code solution, rather than an application property.
… trying to cache components in the session … the key question here is also how much you even save by doing that? My guess is that the majority of all gains could be achieved by caching data rather than component instances since the overhead of creating the component instances is typically small in comparison to the data loading and also overshadowed by the work that anyways happens when an existing component instance is re-attached.
I already cache the data. But if I have a grid with 1,000 rows, why not re-attach that grid instead of populating a new one?
Have you measured the impact?
No. I’ve only just reached the point of discovering that I can’t continue further with my current approach. No point measuring the impact between options when only one option is supportable.
My ideal case would be to set vaadin.allowed-packages to a single package. Inside that package are spring @Component classes. Inside these classes are @Bean methods (with @Scope("prototype") that provide the Vaadin components.
This does not work. And the @Uses annotation seems a bit of a code smell.
The @Bean methods in the @Component classes do work, but only if you also tell Vaadin what to scan using @EnableVaadin.
This sounds like premature optimization. Grids can be slow if they’re very large and complex, but the slowness mainly happens in three places, roughly in this order: 1) loading data from external sources, 2) rendering in the browser, and 3) moving data over the network from the server to the client. None of these cases benefit from reusing a server-side Component instance. Same holds true for memory usage.
The Grid hasn’t actually loaded and processed all those 1000 rows. It keeps a server-side cache with the ~50 rows that are currently visible in the client + a little bit of scroll buffer. The rest hasn’t even been fetched from the data provider. All the processing happens when items are sent to the client and all of that has to happen again whenever the Grid is shown again, e.g. after navigation or a page reload. The result of this processing is anyways not cached on the server so there’s very little to gain from keeping the server-side instance.
The key difference is that the layout was explicitly “torn off” in that case. If you just have a component instance that is implicitly shared, then it will not be properly detached from its previous parent if that parent belongs to a different UI instance. This is something that the framework could detect and handle “correctly” but it has turned out that it’s an even bigger surprise and much harder to debug if the component just disappears from its previous location. That’s why we have made adoption across UI instances require explicit “tear-off”.
The challenge here is that discovery of Vaadin classes also needs to happen during the production build where a Spring context has typically not been available (though the new AoT support is changing this with some lingering limitations). For that reason, we cannot rely on Spring’s discovery mechanism but instead have to use our own that is built in a way that can work in the same way at runtime in development and as part of the build for production.
Have you looked at Building Apps the Vaadin Way? The whole section in the documentation is fairly new and incomplete, but it’s still a very nice guide on how to build apps beyond Hello Worlds. If it’s missing something, I’m sure @Petter would be interested in hearing about it.
Now extend it to show either a list of objects (grid) or a single object (form). Then a third View that shows grid+form together as a List-Detail. Provide a “new instance” and “deleted selected” button. Hook up all the events and navigation. Make sure to add/remove the events in the correct lifecycle phase, i.e., so that everything works when the user navigates using back/forward/history (navigate from any view to any other view including “self/F5” using a url).
Now the user wants to remember the last-used column list and sort order in the grid. So we need to store and restore a grid snapshot in the VaadinSession. Where in the lifecycle (beforeEnter/onDetach/etc) should this go?
This is a pretty good fundamental set of features that all of us (I presume?) need to solve.
P.S. You don’t need the List-Detail View. Just navigating between grid and form using programmatic form, browser history, and url-pasting is sufficient.
Usually I never store a component in the session, because that means logout/login won’t work. That also means working on 2 tabs won’t work.
So I’m building my views to be able to create them based on the URL. If the requirements are to save the grid settings then I will store and load them from the database. I could as a first version save load the grid settings in the session.
The other way is to put the grid in the UIScope. That won’t survive the refresh or session timeout. But that’s cheap to implement.