This project and document contains a collection of tips and tricks that will hopefully make it easier and more fun for you to work with Vaadin.
Note, this is not a challenge. Please do not try to incorporate everything you see here into your project all at once unless it makes sense for your project. As always, use common sense and strive for the simplest solution that solves your problem.
Unless you find some sort of twisted pleasure in scouring Google for jar files, I suggest that you use a tool like Maven to handle your dependencies. It may be fairly straight forward to get your project up and running without, but maintenance will become a big headache in the long run.
If you are using Maven, you can additionally benefit from the archetypes we provide to kickstart your project:
- vaadin-archetype-application - basic application skeleton
- vaadin-archetype-application-multimodule - multimodule project with production and development profiles
- vaadin-archetype-application-example - an example CRUD app with a responsive layout
- vaadin-archetype-widget - startingpoint for building your own widgets
All the archetypes are listed here.
Vaadin comes with a built in Navigator
helper that maps between Views and uri fragments. This allows you to create bookmarkable views within your application. Correctly used, the Navigator
will also help you keep your application memory usage in check.
Don't initialize the Navigator unless a user is logged in. Keep your login view separate from the rest of your navigation, and only create the Navigator once the user has authenticated. This way, you do not inadvertently expose any content to unauthorized users, and you may select which views to register for a certain user (for instance only register an admin view if an admin logged in). See DemoUI.init()
and AwesomeApp#setupNavigator()
.
Once the user is logged in, you want to call navigator.navigateTo(navigator.getState())
to make sure the user is forwarded to the page they were trying to access before getting redirected to the login page.
Define your view names in constants to avoid making typos. In this project, I created a custom annotation ViewConfig
that I used to store the view name, display name (for the menu and page title) and the creation modes (I'll cover creation styles shortly). The main thing to keep in mind is that you do not want to be typing view names by hand in every place you use them.
See OrdersView
for an example use of how I've used the ViewConfig
annotation. The metadata is then used in AwesomeApp.addView
to both register the view to the Navigator
and to add it to the navigation bar.
When registering a view, be very careful how you do it. Doing it wrong may cause your application to use significantly more memory than needed. By default, there are two ways of doing this:
navigator.addView("viewname", View.class)
navigator.addView("viewname", new View())
The difference between these, is that #1 will always give you a new instance of a View when you navigate to it, whereas #2 always returns the same instance. Unless you have very compelling reasons to use #2, you should always use #1. If you use #2, the Navigator needs to keep a reference to the View in order to return it later. This means that no Views can be garbage collected and will significantly increase the memory used per session.
If you have a case where you need the same instance to always be returned, you should defer the instantiation of that View until it is actually needed. This way you don't increase the initial startup time of your application. LazyProvider
contains an example implementation of how to do this.
In this demo project, we define the instantiation mode through the @ViewConfig
annotation. You can see the actual view registration in AwesomeApp.addView
.
ViewChangeListener
is a very handy way of attaching logic to view transitions. In this project, we use them to update the selected menu item in the navigation bar and for setting the Page title. See AwesomeApp.setupNavigator
for the registration of the listeners and NavBar#afterViewChange
and PageTitleUpdater
for implementations.
You can also implement access control by doing checks in a ViewChangeListener
and returning false
from the beforeViewChange
method. Please remember though that the best way of preventing access to views is to not register views in the first place for a user that doesn't have access.
Always! This is the equivalent to a 404 page for your Vaadin application. Without this, any user navigating to an incorrect URL will get an ugly stacktrace.
Especially if you are eagerly instantiating your Views, you should defer any actual data lookup and initialization to the enter method that is shown when the user actually opens the view.
Vaadin is a component based framework. Because everything in Vaadin is a component and defined in Java, you can easily create your own reusable compositions.
For instance -- in this project, instead of having to set up a Grid from scratch in each of the Order examples, I opted to extend Grid and create a more specialized OrderGrid, that already contained the needed column setup and converters I needed. This way, I could keep the View implementations simple and to the point.
When creating a composition, you have two options: using CustomComponent
or just extending an existing component. The benefit of using CustomComponent
is that it hides all internal implementation details and allows you to specify the Component API. The disadvantage of using CustomComponent
is that it will increase complexity, both in your code and in the DOM, which can in some cases have an impact on rendering speed.
In general, I prefer directly extending a component to keep things simple and lightweight. If you are creating a component for someone else to use, you may consider using a CustomComponent to wrap it.
One often overlooked feature of the Vaadin layouts is setMargin
and setSpacing
. In most cases, turning these on will give you a much more visually pleasing result, as all your components aren't squished together. In fact, I use this so often that I created a custom VerticalSpacedLayout
that defaults to having them enabled.
Vaadin comes with a powerful Sass based theme builder called Valo. Valo allows you to customize the look and feel of your application by configuring a set of high-level parameters. You can make large modifications to your application with just a single change, as unspecified values will be automatically populated with reasonable values. For instance, if you change the background color to a dark color, Valo will change text and highlight colors to a lighter color to ensure sufficient contrast.
When working with themes, it is usually a good idea to store CSS class names in a helper class as constants. In this application, we extend the Valo theme, so our theme helper class MyTheme
extends the ValoTheme
helper, to give us access to all the already defined classnames. See LoginBox
for an example of using the helper class.
When building your application, you usually want to add reasonable CSS classnames to relevant layouts and components. This will make changing the look and feel much easier later on.
When styling your application, try to stay away from writing selectors against any .v-
prefixed classnames, as this will make your CSS more bound to the specific implementing class. So, instead of styling .v-vertical-layout
, give your layout a meaningful name with addStylename()
and write your selector for that. This way, you have more freedom in changing the implementing layout without affecting your styles.
Take advantage of the fact that Sass allows you to split up your stylesheets into separate files without incurring any runtime overhead. You will ussually want to keep styles for different views/components in separate files for easier maintenance. When compiled, all your CSS will be in one file.
Remember that you need to manually compile the SCSS before moving to production, as the automatic compilation is disabled in production mode!
A common question in Vaadin development is how one should handle large data sets. In this application, I've created three examples to show the typical approaches to solving the problem. We're working with a order system that has around 10,000 orders and is quite slow. Let's look at the pros and cons of each approach below.
The most straight-forward solution is of course that we just load all the Orders, put them in a Container, and tell our Grid to display the data. This example can be found in OrdersView
.
The problem with loading all data upfront is that the application will feel very sluggish to the user. We will also end up using a significant amount of memory on the server. For systems with a few concurrent users, this may not be an issue worth addressing. But for systems with a lot of concurrent users it will make a difference.
The benefit of this approach is of course it's simplicity. If you are OK with the page taking a while to load and only have a few users on a server concurrently, this simple approach is just fine.
In order to make the application more responsive, we'll improve our solution by delegating the long running application to a background thread and show the user a message and a spinner while we fetch the data.
By immediately loading the page and informing the user what we're doing, the system will feel much more responsive. Users tend to tolerate much longer delays if you inform them what is happening instead of just having the UI hang. Once the data are available, we'll again populate a container and swap out a Grid instead of our placeholder spinner. Source code for this step can be found in AsyncOrdersView
.
Note: When updating the UI from a background thread, we need to use either polling or push to allow the server to initiate a UI update. Remember to use
the getUI().access()
helper to properly handle locking while updating the UI. Also note that helpers such as UI.getCurrent()
which depend on ThreadLocal
s will not work in background threads, so be sure that you pass your background thread all the needed info.
This version is already much better from a usability standpoint, but will still suffer from the same memory usage problem as our first attempt.
In order to address both initial load speed and memory usage, our third option (found in LazyOrdersView
) will only query as much data as it can show in the grid when you open the page, and will fetch more data as needed when you scroll down.
The third option is clearly better than the two previous from a memory consumption perspective. The downside is that the implementation is much more complex to implement, especially if you need to support sorting and filtering. In this example, I based the solution on LazyList
from Viritin. There are other lazy loading container implementations such as the JPAContainer, SQLContainer, and LazyQueryContainer that can help you with the implementation.
Before spending too much time on figuring out how to display 100,000 rows of data in a table, I sincerely suggest that you take a step back and think about what you are doing. Showing thousands of rows of data to a user is rarely the ideal way of presenting data. How often do you look up the business hours of a restaurant by searching 'restaurant' in Google and clicking yourself all the way to page 12,043?
If possible, try to offer your user better search tools or filters so you don't need to deal with huge data sets in the first place. If you only need to show 50 rows instead of 100,000, putting them in an in-memory container won't be an issue anymore.
Another very common question from Vaadin developers is how to create forms easily. I've tried to show you some tips in the FormView
class.
Usually, it is easiest to keep your form in a Component of its own. This way, you can easily tell FieldGroup
to scan the component for your fields to bind. Remember that the names of your fields need to exactly match the property ids in your Item
, otherwise you'll need to annotate them with @PropertyId
, like I did in FormLayout
.
Unless your form only contains strings and numbers, there's a good chance you'll want to create your own Fields to handle the editing of those. In our example, I've created a custom field for editing the list of line items in an Order. You can see the implementation in LineItemField
. The custom field is essentially just an embedded FieldGroup
for editing line items in our order.
By creating our own fields, we can keep the main form simple, and we're able to reuse the implementation in other parts of the software.
Unfortunately, validating forms in Vaadin is not very nice. If you attach a validator to a field, it will already tell the user there's an error before the user has even started filling in the form.
Improving the validation is fairly straight forward, but can get a bit tedious, so I created a helper found in FieldGroupUtil.improveUX
. Calling this method will turn on live validation and make sure that we don't bug our users with validation errors before they've even tried to populate a field.
In addition, the utility will help improve UX by toggling the enabled state of your save/cancel buttons. The cancel button won't be active until a user has made a change to the form, and the save button will only be active once all validations pass.
Rather than getting myself into an ideological warfare over the One Right Way™ of building software, I'll share a couple of practices that can hopefully help you improve your code quality and productivity.
The two things I'll focus on are decoupling and separation concerns.
As you have seen, Vaadin allows you to easily create custom components and compositions so you can work with components that are tailored to your needs. All the reusability benefits of building these compositions fly out the window immediately if your UsefulForm
depends directly on VerySpecificView
. One solution for this is to use an observer pattern and using callback interfaces. This is how Vaadin components work. In order to have a Button
do your work, you give it a ClickListener
and it'll let you know when it was triggered. This is a pattern that you can also use in your own components.
When communicating between different parts of your application, a generic EventBus can be incredibly valuable. An event bus allows you to publish any type of event to a central dispatcher that will then call all registered listeners. So in essence, the same idea as with the ClickListener
, just with a broker in between.
In this sample application you can for instance see that I'm firing NavigationEvent
s from the NavBar. DemoUI
is subscribed to these events and will make sure we get navigated where we need to go. NavBar
is fully unaware of the fact that it is the DemoUI
that implements the functionality. To keep the tech stack as simple as possible in this project, I've used the Google Guava event bus. CDI events in Java EE are also a great way of accomplishing the same.
Please don't go overboard with the use of an event bus. The downside of this level of decoupling is that understanding program flow can become very difficult. So again, use your judgement and use the tools you have responsibly.
Another important topic is separation of concerns. In practice, this tends to manifest itself as a Model View Whatever (MVP, MVC, MVVM) pattern where you separate the UI logic from the layouts and components. This enables us to better test our UI code and makes it easier to find issues in our code as the logic is not split into tens of different UI components.
You can see one very simple MVP example in FormView
/FormPresenter
(Order
is the M in this case). In a real world application you will most likely end up creating abstract View and Presenter classes to handle common use cases like registering views and showing success/error messages.
The actual implementation is in my opinion less important than the fact that you keep the concerns separated.
There are a few levels of testing that we can do with our Vaadin application. Here, we'll look at unit/integration testing and regression testing.
Since we split our FormView
into a View/Presenter pair, we can easily write normal JUnit tests for the Presenter without having to deal with instantiating Vaadin components or figuring out things like how to stub out static calls to VaadinSession.
FormPresenterTest
shows a simple test that uses mock objects instead of both OrderService
and FormView
. This way we have full control over the tests and can exercise things like exception handling without having to manipulate an external system.
Note that by changing at what level you stub out the rest of the system, you can also write integration tests in the same way. We could for instance have used an actual OrderService implementation instead of the mock to exercise the entire system.
On top of the more low-level unit tests, it's good to write some broader regression tests to ensure that you don't break your app by mistake while working on something else. Vaadin TestBench is a tool that is built for creating regression tests for Vaadin apps. It will allow us to build tests that run our application through an actual browser.
FormRegressionTest
shows an example of a test that fills in our order form and submits it. TestBench also allows us to measure performance. In RenderingSpeedTest
we have built a test that ensures a view is rendered quicker than a given threshold.
Note Vaadin TestBench is a commercial add-on for Vaadin. You can obtain free licenses for non-profit use on our web page. There is also a 30 day trial available. In order to run the tests in this project you'll need to have Firefox ESR installed.
We've already seen that the speed of our application can play a big role in how our users perceive our app. In the previous example, we were able to remedy the issue by not loading the entire large dataset upfront. What if the cause for slowness isn't as apparent?
Before we can start fixing the problem, we need to figure out what the problem is. One good starting point is to see whether the slowness is on the server side or in the browser. You can use the Vaadin debug window to do this. Launch your application with the ?debug
parameter and look at the console output. We are interested in two things:
- Server visit took XXms
- Processing time was XXms for NNN characters of JSON
The first tells us how long we spent on the server processing the request, the second tells us how long the browser spent rendering the result. If the time taken on the server is significantly lower than the time for rendering, we'll have to start looking at what might cause rendering slowness.
Slow rendering is almost always caused by unnecessarily complex and deep component hierarchies. If you have complex layouts, see if you could get rid of some wrapping layouts, Panels or CustomComponents.
Try changing the implementation used in SlowRenderingView
to QuickOrderLayout
and you'll see that the render time drops to about a third of what it was initially.
Again, please start with a simple solution and only optimize if you actually identify a problem. Flattening our hierarchy by inlining HTML is similar to optimizing a function in assembler in a C. You will trade code clarity and strong typing for optimization.
If the slowness is on the server, you'll want to use a profiler to see where in the code you're spending most time. Typically it will be in fetching data. You may want to consider asynchronous loading if the delay is very noticeable and you cannot optimize it by tweaking DB indexes or optimizing your service level code.
You will also want to check on your application memory usage to keep things running smoothly in production. Download VisualVM and attach to your running server. See the difference in how memory is freed when changing how you instantiate your Views. Try changing the create mode of HeapDestroyer
from LAZY to ALWAYS_NEW and you'll notice that the second option allows us to immediately free the memory once the user is no longer on the view.
Always try to keep your project as simple as possible. Don't introduce any new technology/pattern/whatever unless there is really a need for it.
Also remember to refactor constantly. Instead of trying to figure out and plan for all possible reusable components up front, start by building concrete implementations. When you feel like you are building the same thing for the second time, it's time to refactor the commonalities into a common class. I feel that working this way will keep you moving quickly and help avoid unnecessary complexity.
Often having a quick 15 minute talk with a Vaadin expert before you start programming can save you a lot of debugging and refactoring later. We're here to help you with anything form short problem solving tasks to long term consulting, just give us a call or email us.