Sample Application with Spring Boot and Vaadin

1. Overview

Vaadin is a server-side Java framework for creating web user interfaces.

In this tutorial, we’ll explore how to use a Vaadin based UI on a Spring Boot based backend. For an introduction to Vaadin refer to this tutorial.

2. Setup

Let’s start by adding Maven dependencies to a standard Spring Boot application:

<dependency>
    <groupId>com.vaadin</groupId>
    <artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>

Vaadin is also a recognized dependency by the Spring Initializer.

This tutorial uses a newer version of Vaadin than the default one brought in by the starter module. To use the newer version, simply define the Vaadin Bill of Materials (BOM) like this:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-bom</artifactId>
            <version>10.0.11</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3. Backend Service

We’ll use an Employee entity with firstName and lastName properties to perform CRUD operations on it:

@Entity
public class Employee {

    @Id
    @GeneratedValue
    private Long id;

    private String firstName;
    private String lastName;
}

Here’s the simple, corresponding Spring Data repository – to manage the CRUD operations:

public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    List<Employee> findByLastNameStartsWithIgnoreCase(String lastName);
}

We declare query method findByLastNameStartsWithIgnoreCase on the EmployeeRepository interface. It will return the list of Employees matching the lastName.

Let’s also pre-populate the DB with a few sample Employees:

@Bean
public CommandLineRunner loadData(EmployeeRepository repository) {
    return (args) -> {
        repository.save(new Employee("Bill", "Gates"));
        repository.save(new Employee("Mark", "Zuckerberg"));
        repository.save(new Employee("Sundar", "Pichai"));
        repository.save(new Employee("Jeff", "Bezos"));
    };
}

4. Vaadin UI


==== 4.1. MainView Class

The MainView class is the entry point for Vaadin’s UI logic. Annotation @Route tells Spring Boot to automatically pick it up and show at the root of the web app:

@Route
public class MainView extends VerticalLayout {
    private EmployeeRepository employeeRepository;
    private EmployeeEditor editor;
    Grid<Employee> grid;
    TextField filter;
    private Button addNewBtn;
}

We can customize the URL where the view is shown by giving a parameter to the @Route annotation:

@Route(value="myhome")

The class uses following UI components to be displayed on the page:

EmployeeEditor editor – shows the Employee form used to provide employee information to create and edit.

Grid<Employee> grid – gird to display the list of Employees

TextField filter – text field to enter the last name based on which the gird will be filtered

Button addNewBtn – Button to add a new Employee. Displays the EmployeeEditor editor.

It internally uses the employeeRepository to perform the CRUD operations.

4.2. Wiring the Components Together

MainView extends VerticalLayout. VerticalLayout is a component container, which shows the subcomponents in the order of their addition (vertically).

Next, we initialize and add the components.

We provide a label to the button with a + icon.

this.grid = new Grid<>(Employee.class);
this.filter = new TextField();
this.addNewBtn = new Button("New employee", VaadinIcon.PLUS.create());

We use HorizontalLayout to horizontally arrange filter text field and the button. Then add this layout, gird, and editor into the parent vertical layout:

HorizontalLayout actions = new HorizontalLayout(filter, addNewBtn);
add(actions, grid, editor);

Provide the gird height and column names. We also add help text in the text field:

grid.setHeight("200px");
grid.setColumns("id", "firstName", "lastName");
grid.getColumnByKey("id").setWidth("50px").setFlexGrow(0);

filter.setPlaceholder("Filter by last name");

On the application startup, UI would look this:

image

4.3. Adding Logic to Components

We’ll set ValueChangeMode.EAGER to the filter text field. This syncs the value to the server each time it’s changed on the client.

We also set a listener for the value change event, which returns the filtered list of employees based on the text provided in the filter:

filter.setValueChangeMode(ValueChangeMode.EAGER);
filter.addValueChangeListener(e -> listEmployees(e.getValue()));

On selecting a row within the gird, we would show the Employee form, allowing the user to edit the first name and last name:

grid.asSingleSelect().addValueChangeListener(e -> {
    editor.editEmployee(e.getValue());
});

On clicking the add new employee button, we would show the blank Employee form:

addNewBtn.addClickListener(e -> editor.editEmployee(new Employee("", "")));

Finally, we listen to the changes made by the editor and refresh the grid with data from the backend:

editor.setChangeHandler(() -> {
    editor.setVisible(false);
    listEmployees(filter.getValue());
});

The listEmployees function gets the filtered list of Employees and updates the grid:

void listEmployees(String filterText) {
    if (StringUtils.isEmpty(filterText)) {
        grid.setItems(employeeRepository.findAll());
    } else {
        grid.setItems(employeeRepository.findByLastNameStartsWithIgnoreCase(filterText));
    }
}

4.4. Building the Form

We’ll use a simple form for the user to add/edit an employee:

@SpringComponent
@UIScope
public class EmployeeEditor extends VerticalLayout implements KeyNotifier {

    private EmployeeRepository repository;
    private Employee employee;

    TextField firstName = new TextField("First name");
    TextField lastName = new TextField("Last name");

    Button save = new Button("Save", VaadinIcon.CHECK.create());
    Button cancel = new Button("Cancel");
    Button delete = new Button("Delete", VaadinIcon.TRASH.create());

    HorizontalLayout actions = new HorizontalLayout(save, cancel, delete);
    Binder<Employee> binder = new Binder<>(Employee.class);
    private ChangeHandler changeHandler;
}

The @SpringComponent is just an alias to Springs @Component annotation to avoid conflicts with Vaadins Component class.

The @UIScope binds the bean to the current Vaadin UI.

Currently, edited Employee is stored in the employee member variable. We capture the Employee properties through firstName and lastName text fields.

The form has three button – save, cancel and delete.

Once all the components are wired together, the form would look as below for a row selection:

image

We use a Binder which binds the form fields with the Employee properties using the naming convention:

binder.bindInstanceFields(this);

We call the appropriate EmployeeRepositor method based on the user operations:

void delete() {
    repository.delete(employee);
    changeHandler.onChange();
}

void save() {
    repository.save(employee);
    changeHandler.onChange();
}

5. Conclusion

In this article, we wrote a full-featured CRUD UI application using Spring Boot and Spring Data JPA for persistence.

As usual, the code is available over on GitHub.

Leave a Reply

Your email address will not be published.