Accessibility Now
Join our upcoming webinar about building accessible web applications! June 7, 2022.
Blog

Vaadin Key Concepts

By  
Marcus Hellberg
Marcus Hellberg
·
On Dec 1, 2019 11:47:00 AM
·

This document is meant for developers who are new to Vaadin and want to get up to speed quickly. It gives you a quick overview of the core concepts and provides links to relevant documentation for further reading. It can also be used as a quick reference while developing Vaadin apps.

If you don’t have Vaadin app set up yet, you can download one on the starter page.

Core concepts

1. Everything is a component

Need a button? Write new Button(). Need a text field? Write new TextField().

Build own components and views by composing existing components with layouts.

Navigate to components by marking them with a @Route("path") annotation.

2. Listen to events to make your app interactive

Add a listener to a button with addClickListener() or get notified of changed selection in a select with addValueChangeListener().

Any component that a user can interact with will dispatch events that you can listen to.

Hello world

Here is a small but complete Vaadin application.

MainView.java
@Route("")
public class MainView extends VerticalLayout {
public MainView() {
add(new H1("Hello World!"));
}
}

Vaadin uses a component-based programming model. In this example, our application is a component that extends from one of Vaadin’s basic layouts, VerticalLayout. In the constructor, we add an H1 component (corresponds to a <h1> HTML tag) to the layout to say hello to the entire world.

Finally, we map the application to the context root with the @Route("") annotation. The empty string here means that it should match the empty route.

Components

Vaadin has a component-based programming model. It comes with a large library of UI components and layouts that you can extend with your own components to build applications.

Note
If you are using one of the starters, you have access to all the components without any additional installation steps. The installation instructions on the component pages are for those who are setting up a custom project setup from scratch.

Using components

Let’s begin by instantiating a new component:

Button button = new Button("Click me");
button.setIcon(VaadinIcon.VAADIN_V.create());

In Vaadin, each component is represented by a Java object. To use a component, create a new instance and configure it. The easiest way to explore the functionality of components is through the autocomplete menu in your IDE.

Having a component all by itself is not that useful. To build something more meaningful, we’ll want to create composites of several components that interact with each other. To do that, we need layouts.

Layouts

Layouts are special components that hold other components and determine how the components get shown in the browser. Because layouts are components themselves, they can be nested to create more complex layouts.

The most common layouts you will encounter in Vaadin applications are:

VerticalLayout and HorizontalLayout

VerticalLayout and HorizontalLayout are the workhorses of most Vaadin applications. As their name suggests, they place child components either vertically on top of each other or horizontally next to each other.

By default, the layouts are only as big as their contents and will add spacing between components to make the layout more natural to read and use. The spacing can be turned on or off with setPadding(false) for the spacing around the layout or setSpacing(false) for the spacing between components.

A common requirement is to have one component expand to use up all the space not needed by other components. This can be achieved with the expand(component) method on the layout.

Here is an example of the concepts in action to build a common app layout. In this example, we are setting widths and heights programmatically through Java. You can also do this via CSS, which we will cover later in this guide.

BasicLayoutView.java
@Route("")
public class BasicLayoutView extends VerticalLayout {

public BasicLayoutView() {
// Instantiate layouts
HorizontalLayout header = new HorizontalLayout();
VerticalLayout navBar = new VerticalLayout();
VerticalLayout content = new VerticalLayout();
HorizontalLayout center = new HorizontalLayout();
HorizontalLayout footer = new HorizontalLayout();

// Configure layouts
setSizeFull();
setPadding(false);
setSpacing(false);
header.setWidth("100%");
header.setPadding(true);
center.setWidth("100%");
navBar.setWidth("200px");
content.setWidth("100%");
footer.setWidth("100%");
footer.setPadding(true);

// Compose layout
center.add(navBar, content);
center.setFlexGrow(1, navBar);
add(header, center, footer);
expand(center);
}
}
Basic header
Note
HorizontalLayout and VerticalLayout are not well suited for responsive layouts. They will not wrap their content if the viewport gets too narrow. For that, we’ll need to use some CSS.

Div

The Div is the most flexible of all the layouts, but it also requires you to handle all positioning yourself through CSS. We can use the CSS flexbox layout to achieve a smooth responsive layout that adapts to different viewport sizes.

Responsive layout featuring a header

When we are using CSS to layout components, we need to define CSS classnames for all the components. We also need to add a @StyleSheet annotation to load a CSS file.

DivLayoutView.java
@Route("div")
@StyleSheet("frontend://styles/div-layout-styles.css")
public class DivLayoutView extends Div {

public DivLayoutView() {
// Instantiate layouts
HorizontalLayout header = new HorizontalLayout();
VerticalLayout navBar = new VerticalLayout();
VerticalLayout content = new VerticalLayout();
Div center = new Div();
HorizontalLayout footer = new HorizontalLayout();

// Configure layouts
setSizeFull();
header.setPadding(true);
footer.setPadding(true);
addClassName("main-view");
header.addClassName("header");
navBar.addClassName("navbar");
center.addClassName("center");
content.addClassName("content");
footer.addClassName("footer");

// Compose layout
center.add(navBar, content);
add(header, center, footer);
}
}

Create the CSS file in the frontend/src/styles directory. This may either be webapp/frontend/src/styles/ or META_INF/resources/frontend/src/styles/ depending on the starter you are using.

div-layout-styles.css
.main-view {
display: flex;
flex-direction: column;
}

.center {
flex: 1;
display: flex;
flex-wrap: wrap;
}

.navbar {
flex: 1;
min-width: 200px;
}

.content {
flex: 4;
min-width: 500px;
}

Depending on your previous development background, you may find this approach cleaner than the earlier programmatic way. If you are working with designers as a part of your team, the CSS based view is going to be easier for them to configure without having to touch Java code.

Tip
Vaadin has a Java wrapper for the CSS flexbox layout, FlexLayout. It allows you to set the same flex attributes from Java instead of having a separate CSS file.

Declarative layouts

Sometimes you may find it more convenient to define a layout declaratively in HTML. Vaadin supports this through the Template API.

Declarative layouts and Templates are considered a more advanced topic. You can learn more about creating declarative layouts in the documentation.

Listening to events

Vaadin is an event-driven framework. Any component that a user can interact with will emit events that you can listen to. A typical event you will listen to is the click event emitted by a Button.

Button button = new Button("Click me");
button.addClickListener(click -> {
Notification.show("Thanks for clicking!");
});

In this example, we show a popup notification to the user when they click the button.

Tip
You can explore the different listeners that are available in components by looking at the add*Listener methods available in the autocomplete menu on the component you are using.

Creating components

So far, we have only looked at using existing components.

When you are developing your own Vaadin applications, you will often create your own components to encapsulate complex parts of the system into reusable components. This forms the base of the component-based programming model that Vaadin and most other modern UI frameworks use.

Creating components through composition

The most commonly used way of creating a new component is to create a composition of existing components with an appropriate API for what you are building.

Let’s look at a simple example of a component that displays a business card.

BusinessCard.java
public class BusinessCard extends VerticalLayout {
private Span name = new Span();
private Span title = new Span();

public BusinessCard() {
add(name, title);
}

public void setName(String n) {
name.setText(n);
}

public void setTitle(String t) {
title.setText(t);
}
}

Once we have created this component, we can now import it and use it in our application:

ComponentView.java
@Route("component")
public class ComponentView extends VerticalLayout {

public ComponentView() {
BusinessCard businessCard = new BusinessCard();
businessCard.setName("Marcus");
businessCard.setTitle("Computer code maker");

add(businessCard);
}
}
Note
While creating composite components by extending from an existing layout is common, it has one drawback: you are exposing all of the API of the component you are extending. This may not be an issue in your project, but if you are creating a component that will be reused widely and you don’t want people to mess around with the internals, you can enforce a stronger encapsulation with Composite.
BusinessCard.java
public class BusinessCard extends Composite<VerticalLayout> {
private Span name = new Span();
private Span title = new Span();

public BusinessCard() {
getContent().add(name, title);
}

public void setName(String n) {
name.setText(n);
}

public void setTitle(String t) {
title.setText(t);
}
}

In this second version, we are hiding the fact that we are internally using VerticalLayout, only exposing the setName and setTitle methods as public API. Vaadin will internally create the VerticalLayout, that we can get using getContent(). Components used as composites need to have a no-arg constructor so Vaadin can instantiate them.

Creating components with Vaadin Designer

The Vaadin Designer visual builder is a tool that allows you to build components and views with a drag-and-drop interface instead of code. It can be a powerful tool when you need to create many views or big forms.

You can use components created by Designer in your code like any other component. You can also use the components you have hand-coded in Designer.

Interacting with JavaScript events and DOM nodes

In some cases, you need to build a component that cannot be built by just composing existing components.

Vaadin is built to be easily extendable to help in situations like this. By using the Element API, you can get access to DOM-level events and attributes/properties.

As an example, let’s listen for a mouseover event on a component and log it to the console:

businessCard.getElement().addEventListener("mouseover", e -> {
System.out.println("'mouseover' event happened");
});

Creating custom JavaScript components

You can also write or integrate completely custom UI components in Vaadin. These are more advanced topics, and are covered in more detail in the documentation:

Forms and data binding

Binding data from data objects to inputs is one of the most central functions of most applications. Vaadin has a powerful data binding API that allows you to create any kind of form you can imagine.

The Vaadin Binder API supports validating both individual fields and cross-field validation of the entire form. You can also define conversions between the value you show to the user and the value you save to the object.

Setting up data binding

Binder works with standard Java objects as the data model. It allows you to bind any property (getter/setter) to a UI field.

Binder supports both one-way data binding (readBean(model)) or two-way data binding (setBean(model)). Read the section on saving to learn how to get the value out of Binder if you are using one-way data binding.

FormView.java
@Route("forms")
public class FormView extends FormLayout {

public FormView() {
Person model = new Person();

TextField firstNameField = new TextField("First Name");
Binder<Person> binder = new Binder<>(Person.class);

binder.forField(firstNameField).bind(Person::getFirstName, Person::setFirstName);

// One-way data binding
// binder.readBean(model);

// Two-way data binding
binder.setBean(model);
}
}

Validating input fields

Most of the time when building forms, we need to validate that the input we receive is correct. In Vaadin, this is done by adding a Validator to the field binding.

binder.forField(firstNameField)
.asRequired()
.withValidator(name -> name.equals("Marcus"), "Your name should be Marcus")
.bind(Person::getFirstName, Person::setFirstName);

Here, we make the input field mandatory and require the input value to equal "Marcus." You can add as many validators as you want, they will get run in the order you define them.

Converting between presentation and model values

If your underlying data type does not match the data type of the UI component, you need to add a converter to the binding. In the following example, we want to bind a TextField (String) to an underlying int data field, so we need to add a StringToIntegerConverter.

binder.forField(ageField)
.asRequired()
.withConverter(new StringToIntegerConverter("Age must be a number"))
.withValidator(age -> age > 2, "How are you using a computer?")
.bind(Person::getAge, Person::setAge);
Tip
There are several built-in converters for common conversions. Look for *Converter in your IDE autocomplete menu.

Validating forms (cross-field validation)

The same way you can add validations to individual fields, you can also add validators on the binder to validate fields against each other. Binder-level validation will only run if field level validation has passed.

We also need to define a layout in which we want error messages to be displayed.

Div statusLabel = new Div();
binder.setStatusLabel(statusLabel);
binder.withValidator(person ->
person.getFirstName().equals("Marcus")
&& person.getLastName().equals("Hellberg"),
"Ha! You have the correct first name, but wrong last name. Better luck next life!");

Saving

Two-way data binding

If you used two-way data binding (setBean()), the bound object would contain the updated values, provided that all field and form level validations passed.

One-way data binding

If you used one-way data binding (readBean()), you need to read the form manually.

try {
Person saved = new Person();
binder.writeBean(saved);
// Persist saved bean
} catch (ValidationException e){
//show errors to user
}

You need to pass Binder an instance to write the values to. If you are using one-way data binding, you usually do not want to pass in the original object, as this would mutate it.

Displaying and lazy loading lists of data

Especially in business applications, it’s common to deal with lists of data. In Vaadin, this is commonly done with the Grid or ComboBox components.

In-memory list

If you have a small number of items, say a few hundred, the easiest way to deal with these is to just pass a java Collection to the setItems method.

Note
Vaadin runs the UI code on the server. One of the most common reasons for performance issues is that developers keep references to large collections in their components. If you need to display more extensive collections, or are memory conscious, look at using the alternative DataProvider API below.
GridView.java
@Route("grid")
public class GridView extends VerticalLayout {

public GridView(AddressBook addressBook) {
Grid<Person> grid = new Grid<>();
grid.addColumn(Person::getFirstName).setHeader("First Name");
grid.addColumn(Person::getLastName).setHeader("Last Name");
grid.addColumn(Person::getAge).setHeader("Age");

List<Person> everyone = addressBook.findAll();
grid.setItems(everyone);

add(grid);
}
}
Note
The example above uses dependency injection to inject AddressBook in the constructor. Read more about using Vaadin with Spring, or using Vaadin with CDI. You can also download pre-configured starters on the starter page.

Lazy-loading with DataProvider

In cases where you have much data, it makes sense to only load what’s needed at a given moment. Vaadin components like Grid and ComboBox do this automatically if you define a DataProvider.

Instead of calling setItems() as we did in the previous snippet, use setDataProvider().

GridView.java
grid.setDataProvider(DataProvider.fromCallbacks(
findQuery -> addressBook.find(
findQuery.getOffset(),
findQuery.getLimit()),
countQuery -> addressBook.count()));

The data provider takes two callback functions. The first query should return a stream of objects with a given offset and limit, the next page of data. The second query returns the total count of objects in your data set. This is used to display appropriate scrollbars and info in the UI.

The end user experience is the same whether you use setItems() or setDataProvider(). In both cases, the user can scroll freely through the entire data set. The only difference is that when using DataProvider, you are using fewer resources on the server.

Views and navigation

Vaadin comes with a simple, yet powerful navigation system. Any component can be made a navigation target, and you can easily capture URL parameters.

Defining routes

To define a route, annotate your class with @Route.

SampleView
@Route
public class SampleView extends VerticalLayout {

public SampleView() {
add(new H1("I am a sample view"));
}
}

By default, Vaadin will determine the route path based on the class name, without the -View ending. In this case, the view would get mapped to /sample.

In many cases, it’s better to be explicit about the naming and define the path explicitly. For this, it’s recommended to use a constant to support refactoring later on.

SampleView
@Route(SampleView.NAME)
public class SampleView extends VerticalLayout {
public static final String NAME = "sample";

public SampleView() {
add(new H1("I am a sample view"));
}
}

Navigating between views

Once you have a view defined, you can navigate to it in two ways. Using a router link, or programmatically.

To use a router link, add a RouterLink component to a layout.

add(new RouterLink("Go to the sample view!", SampleView.class));

The advantage of router links is that they will continue to work even if the server session has expired.

In some cases, you will need to navigate to another view programmatically. For instance, take the user to another view after filling a form. To do this, use ui.navigate().

Button saveButton = new Button("Save");
saveButton.addClickListener(click -> {
// (Save form logic omitted)

// On success, navigate to another view
saveButton.getUI().ifPresent(ui -> ui.navigate(SampleView.NAME));
});
Note
The UI is the invisible "root" of a Vaadin application. There is one UI per browser tab. The UI is mostly used for navigating, or synchronizing access when updating the UI state from an external thread.

Nested routes, parameters, error pages

You can do much more sophisticated navigation systems than above. Please refer to the documentation to learn more about nested routes, URL parameters, and handling navigation exceptions.

Testing

Testing is a vital part of developing reliable applications. There are typically three types of tests that you will write for your application: unit tests, integration tests, and end-to-end system tests.

Let’s look at an example. Here is the component we are testing:

ExampleComponent.java
@Route
public class ExampleComponent extends VerticalLayout {
private final Binder<Person> binder;
private BackendService service;

TextField firstName = new TextField("First Name");
TextField lastName = new TextField("Last Name");
Button saveButton = new Button("Save");

public ExampleComponent(BackendService service) {
this.service = service;
add(firstName, lastName, saveButton);
binder = new Binder<>(Person.class);
// Bind fields to bean by name
binder.bindInstanceFields(this);
saveButton.addClickListener(click -> save());
}

public void save() {
try{
Person person = new Person();
binder.writeBean(person);
service.save(person);
saveButton.setText("Saved");
} catch (ValidationException e) {
e.printStackTrace();
}
}
}

It is a form with text fields for first and last name, bound to a Person object through a Binder. See the section above on forms if you are not familiar with Binder.

When the form is saved, the resulting Person object should be saved to BackendService.

Unit and integration testing

Unit and integration tests usually make up the bulk of the tests for your application. The tests do not require launching a server or a browser, so they are fast to run.

Unit tests and integration tests are very similar in their implementation. The main difference is that in a unit test, you test a single piece of code in isolation (for instance a component), whereas in an integration test you test several interconnected pieces of content together.

To write a unit test for the component above, we need a testing library to run the code and a mocking library to stub out dependencies to functionality that is not being tested.

In this example, we are using jUnit 5 and Mockito.

ExampleComponentTest.java
class ExampleComponentTest {

private ExampleComponent component;
private BackendService serviceMock;

@BeforeEach
void setUp() {
serviceMock = Mockito.mock(BackendService.class);
component = new ExampleComponent(serviceMock);
}

@Test
void save() {
component.firstName.setValue("Marcus");
component.lastName.setValue("Hellberg");

component.save();

// Person.equals is based on names in this example
Mockito.verify(serviceMock).save(new Person("Marcus", "Hellberg"));
}
}

In the test, we set up a mock version of BackendService as we are focusing our test on ExampleComponent. All we need is to verify that it will get called with the right input.

In the test, we set up the UI state by entering values into the text fields. We then call save() and verify that the backend service would have been called with a Person object with the correct first and last names.

We could also have made this into an integration test by using a real implementation of BackendService instead of stubbing it out.

End-to-end testing (in-browser testing)

In end-to-end testing, we ensure that the complete application works as a whole. To do this, we need to set up a server to deploy the application and set up a browser testing environment.

Vaadin has an official browser testing tool called TestBench. It takes care of launching browsers and has Vaadin-specific selectors for finding UI components in the browser.

To test the component in its real environment through a browser, we could write the following TestBench test case.

ExampleComponentIT.java
public class ExampleComponentIT extends TestBenchTestCase {

@Before
public void setup() throws Exception {
setDriver(new ChromeDriver());
getDriver().get("http://localhost:8080/example");
}

@Test
public void clickButton() {
ButtonElement button = $(ButtonElement.class).first();
button.click();
Assert.assertEquals("Saved", button.getText());
}

@After
public void tearDown() throws Exception {
getDriver().quit();
}
}

Before running the test, we set up a new Chrome browser driver and open the route to the component.

We then find the button, click on it, and assert that the caption got updated correctly.

Note
Vaadin TestBench tests are by convention named with an -IT ending (TestNameIT) that stands for integration test, even though they are technically system tests.

As you can see, this test is very coarse-grained. It can only tell us if everything worked, or if something failed. It won’t give us much information on what went wrong. Because of this, it’s good to have a comprehensive suite of fine-grained unit and integration tests to pinpoint issues. TestBench is better used for high-level, last line of defense, tests that can catch errors even if they are missed by other tests.

Styling with CSS

All Vaadin components ship with a configurable theme called Lumo. Lumo allows you to configure things like colors, fonts, sizing, roundness, and spacing to make the component set look the way you want. The configuration is based on CSS custom properties (variables), which ensures all components look consistent.

Editor used to change colors and visual appearance of a Vaadin app

The easiest way to explore the options and configure values for the CSS variables is to use the Lumo theme editor.

Vaadin apps can further be customized with CSS. Read more about styling Vaadin applications.

Production

When you are ready to take your application into production, run:

mvn clean package -Pproduction

This will build a minified bundle and create an ES5 version that is needed for IE11 support.

Note
This assumes you are using an app based on a Vaadin starter. If not, you need to add the Vaadin Maven plugin manually, see instructions here.

Next steps

Now that you know the basics, you can start building your own app.

If you want to learn how to build a complete app with data binding and a database, start with the basic Vaadin tutorial.

If you prefer watching videos, we have a free online video course on Vaadin basics (requires vaadin.com account).

If you want to browse all tutorials and the learning material, head over to the Vaadin Learning Center.

If you just want to tinker around, check out the code snippets for components or head over to the full documentation for a more in-depth explanation of how Vaadin works.

Marcus Hellberg
Marcus Hellberg
Marcus works as the Director of Developer Relations at Vaadin. His daily work includes everything from writing blogs and tech demos to attending events and giving presentations on all things Vaadin and web-related. You can reach out to him on Twitter @marcushellberg.
Other posts by Marcus Hellberg