You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This guide walks you through the process of building an application that uses a Vaadin based UI on a Spring Data JPA based backend.
What you’ll build
You’ll build a Vaadin UI for a simple JPA repository. What you’ll get is an app with full CRUD (Create, Read, Update, Delete) functionality and a filtering example that uses a custom repository method.
You can start from two different parts, either by starting from the "initial" project you have set up or from a fresh start. The differences are discussed below.
How to complete this guide
Like most Spring Getting Started guides, you can start from scratch and complete each step, or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.
Download and unzip the source repository for this guide, or clone it using Git: git clone https://github.com/spring-guides/gs-crud-with-vaadin.git
It collects all the jars on the classpath and builds a single, runnable "über-jar", which makes it more convenient to execute and transport your service.
It searches for the public static void main() method to flag as a runnable class.
It provides a built-in dependency resolver that sets the version number to match Spring Boot dependencies. You can override any version you wish, but it will default to Boot’s chosen set of versions.
Build with Maven
First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Maven is included here. If you’re not familiar with Maven, refer to Building Java Projects with Maven.
Create the directory structure
In a project directory of your choosing, create the following subdirectory structure; for example, with mkdir -p src/main/java/hello on *nix systems:
It collects all the jars on the classpath and builds a single, runnable "über-jar", which makes it more convenient to execute and transport your service.
It searches for the public static void main() method to flag as a runnable class.
It provides a built-in dependency resolver that sets the version number to match Spring Boot dependencies. You can override any version you wish, but it will default to Boot’s chosen set of versions.
This example is a continuation from Accessing Data with JPA. The only difference is that the entity class has getters and setters and the custom search method in the repository is a bit more graceful for end users. You don’t have to read that guide to walk through this one, but you can if you wish.
If you started with a fresh project, then add the following entity and repository objects and you’re good to go. In case you started with from the "initial" step, these are already available for you.
You can leave the Spring Boot based application intact as it will fill our DB with some example data.
src/main/java/hello/Application.java
packagehello;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.boot.CommandLineRunner;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.context.annotation.Bean;
@SpringBootApplicationpublicclassApplication {
privatestaticfinalLoggerlog = LoggerFactory.getLogger(Application.class);
publicstaticvoidmain(String[] args) {
SpringApplication.run(Application.class);
}
@BeanpublicCommandLineRunnerloadData(CustomerRepositoryrepository) {
return (args) -> {
// save a couple of customersrepository.save(newCustomer("Jack", "Bauer"));
repository.save(newCustomer("Chloe", "O'Brian"));
repository.save(newCustomer("Kim", "Bauer"));
repository.save(newCustomer("David", "Palmer"));
repository.save(newCustomer("Michelle", "Dessler"));
// fetch all customerslog.info("Customers found with findAll():");
log.info("-------------------------------");
for (Customercustomer : repository.findAll()) {
log.info(customer.toString());
}
log.info("");
// fetch an individual customer by IDCustomercustomer = repository.findById(1L).get();
log.info("Customer found with findOne(1L):");
log.info("--------------------------------");
log.info(customer.toString());
log.info("");
// fetch customers by last namelog.info("Customer found with findByLastNameStartsWithIgnoreCase('Bauer'):");
log.info("--------------------------------------------");
for (Customerbauer : repository
.findByLastNameStartsWithIgnoreCase("Bauer")) {
log.info(bauer.toString());
}
log.info("");
};
}
}
Vaadin dependencies
If you checked out the "initial" state project, you have all necessary dependencies already set up, but lets look at what you need to do to add Vaadin support to a fresh Spring project. Vaadin Spring integration contains a Spring boot starter dependency collection, so all you must do is to add this Maven snippet or a similar Gradle configuration:
The example uses a newer version of Vaadin, than the default one brought in by the starter module. To use a newer version, define the Vaadin Bill of Materials (BOM) like this:
The MainView class is the entry point for Vaadin’s UI logic. In Spring Boot applications you just need to annotate it with @Route and it will be automatically picked up by Spring and shown at the root of your web app. You can customize the URL where the view is shown by giving a parameter to the Route annotation. A simple "hello world" could look like this:
For a nice layout, use the Grid component. The list of entities from a constructor-injected CustomerRepository can simply be passed to the Grid using the setItems method. The body of your MainView would expand like this:
If you have large tables and lots of concurrent users, you most likely don’t want to bind the whole dataset to your UI components.
Although Vaadin Grid lazy load the data from the server to the browser, this solution above keeps the whole list of data in the server memory. To save some memory, you could show only the topmost results, use paging or provide a lazy loading data provider using the setDataProvider(DataProvider) method.
Filtering the data
Before the large data set becomes a problem to your server, it will cause a headache for your users trying to find the relevant row he or she wants to edit. Use a TextField component to create a filter entry. First, modify the listCustomer() method to support filtering:
This is where Spring Data’s declarative queries come in real handy. Writing findByLastNameStartsWithIgnoringCase is a single line definition in CustomerRepository.
Hook a listener to the TextField component and plug its value into that filter method. The ValueChangeListener is called automatically during typing as we define the ValueChangeMode.EAGER to the filter text field.
TextFieldfilter = newTextField();
filter.setPlaceholder("Filter by last name");
filter.setValueChangeMode(ValueChangeMode.EAGER);
filter.addValueChangeListener(e -> listCustomers(e.getValue()));
add(filter, grid);
Define the editor component
As Vaadin UIs are just plain Java code, there is no excuse to not write re-usable code from the beginning. Define an editor component for your Customer entity. You’ll make it a Spring-managed bean so you can directly inject the CustomerRepository to the editor and tackle the C, U, and D parts or our CRUD functionality.
src/main/java/hello/CustomerEditor.java
packagehello;
importcom.vaadin.flow.component.Key;
importcom.vaadin.flow.component.KeyNotifier;
importcom.vaadin.flow.component.button.Button;
importcom.vaadin.flow.component.icon.VaadinIcon;
importcom.vaadin.flow.component.orderedlayout.HorizontalLayout;
importcom.vaadin.flow.component.orderedlayout.VerticalLayout;
importcom.vaadin.flow.component.textfield.TextField;
importcom.vaadin.flow.data.binder.Binder;
importcom.vaadin.flow.spring.annotation.SpringComponent;
importcom.vaadin.flow.spring.annotation.UIScope;
importorg.springframework.beans.factory.annotation.Autowired;
/** * A simple example to introduce building forms. As your real application is probably much * more complicated than this example, you could re-use this form in multiple places. This * example component is only used in MainView. * <p> * In a real world application you'll most likely using a common super class for all your * forms - less code, better UX. */@SpringComponent@UIScopepublicclassCustomerEditorextendsVerticalLayoutimplementsKeyNotifier {
privatefinalCustomerRepositoryrepository;
/** * The currently edited customer */privateCustomercustomer;
/* Fields to edit properties in Customer entity */TextFieldfirstName = newTextField("First name");
TextFieldlastName = newTextField("Last name");
/* Action buttons */// TODO why more code?Buttonsave = newButton("Save", VaadinIcon.CHECK.create());
Buttoncancel = newButton("Cancel");
Buttondelete = newButton("Delete", VaadinIcon.TRASH.create());
HorizontalLayoutactions = newHorizontalLayout(save, cancel, delete);
Binder<Customer> binder = newBinder<>(Customer.class);
privateChangeHandlerchangeHandler;
@AutowiredpublicCustomerEditor(CustomerRepositoryrepository) {
this.repository = repository;
add(firstName, lastName, actions);
// bind using naming conventionbinder.bindInstanceFields(this);
// Configure and style componentssetSpacing(true);
save.getElement().getThemeList().add("primary");
delete.getElement().getThemeList().add("error");
addKeyPressListener(Key.ENTER, e -> save());
// wire action buttons to save, delete and resetsave.addClickListener(e -> save());
delete.addClickListener(e -> delete());
cancel.addClickListener(e -> editCustomer(customer));
setVisible(false);
}
voiddelete() {
repository.delete(customer);
changeHandler.onChange();
}
voidsave() {
repository.save(customer);
changeHandler.onChange();
}
publicinterfaceChangeHandler {
voidonChange();
}
publicfinalvoideditCustomer(Customerc) {
if (c == null) {
setVisible(false);
return;
}
finalbooleanpersisted = c.getId() != null;
if (persisted) {
// Find fresh entity for editingcustomer = repository.findById(c.getId()).get();
}
else {
customer = c;
}
cancel.setVisible(persisted);
// Bind customer properties to similarly named fields// Could also use annotation or "manual binding" or programmatically// moving values from fields to entities before savingbinder.setBean(customer);
setVisible(true);
// Focus first name initiallyfirstName.focus();
}
publicvoidsetChangeHandler(ChangeHandlerh) {
// ChangeHandler is notified when either save or delete// is clickedchangeHandler = h;
}
}
In a larger application you could then use this editor component in multiple places. Also note, that in large applications, you might want to apply some common patterns like MVP to structure your UI code (which is outside the scope of this guide).
Wire the editor
In the previous steps you have already seen some basics of component-based programming. Using a Button and selection listener to Grid, you can fully integrate our editor to the main view. The final version of the MainView class looks like this:
src/main/java/hello/MainView.java
packagehello;
importcom.vaadin.flow.component.button.Button;
importcom.vaadin.flow.component.grid.Grid;
importcom.vaadin.flow.component.icon.VaadinIcon;
importcom.vaadin.flow.component.orderedlayout.HorizontalLayout;
importcom.vaadin.flow.component.orderedlayout.VerticalLayout;
importcom.vaadin.flow.component.textfield.TextField;
importcom.vaadin.flow.data.value.ValueChangeMode;
importcom.vaadin.flow.router.Route;
importcom.vaadin.flow.spring.annotation.UIScope;
importorg.springframework.util.StringUtils;
@RoutepublicclassMainViewextendsVerticalLayout {
privatefinalCustomerRepositoryrepo;
privatefinalCustomerEditoreditor;
finalGrid<Customer> grid;
finalTextFieldfilter;
privatefinalButtonaddNewBtn;
publicMainView(CustomerRepositoryrepo, CustomerEditoreditor) {
this.repo = repo;
this.editor = editor;
this.grid = newGrid<>(Customer.class);
this.filter = newTextField();
this.addNewBtn = newButton("New customer", VaadinIcon.PLUS.create());
// build layoutHorizontalLayoutactions = newHorizontalLayout(filter, addNewBtn);
add(actions, grid, editor);
grid.setHeight("300px");
grid.setColumns("id", "firstName", "lastName");
grid.getColumnByKey("id").setWidth("50px").setFlexGrow(0);
filter.setPlaceholder("Filter by last name");
// Hook logic to components// Replace listing with filtered content when user changes filterfilter.setValueChangeMode(ValueChangeMode.EAGER);
filter.addValueChangeListener(e -> listCustomers(e.getValue()));
// Connect selected Customer to editor or hide if none is selectedgrid.asSingleSelect().addValueChangeListener(e -> {
editor.editCustomer(e.getValue());
});
// Instantiate and edit new Customer the new button is clickedaddNewBtn.addClickListener(e -> editor.editCustomer(newCustomer("", "")));
// Listen changes made by the editor, refresh data from backendeditor.setChangeHandler(() -> {
editor.setVisible(false);
listCustomers(filter.getValue());
});
// Initialize listinglistCustomers(null);
}
// tag::listCustomers[]voidlistCustomers(StringfilterText) {
if (StringUtils.isEmpty(filterText)) {
grid.setItems(repo.findAll());
}
else {
grid.setItems(repo.findByLastNameStartsWithIgnoreCase(filterText));
}
}
// end::listCustomers[]
}
Summary
Congratulations! You’ve written a full featured CRUD UI application using Spring Data JPA for persistence. And you did it without exposing any REST services or having to write a single line of JavaScript or HTML.
Creating CRUD UI with Vaadin
This guide walks you through the process of building an application that uses a Vaadin based UI on a Spring Data JPA based backend.
What you’ll build
You’ll build a Vaadin UI for a simple JPA repository. What you’ll get is an app with full CRUD (Create, Read, Update, Delete) functionality and a filtering example that uses a custom repository method.
You can start from two different parts, either by starting from the "initial" project you have set up or from a fresh start. The differences are discussed below.
How to complete this guide
Like most Spring Getting Started guides, you can start from scratch and complete each step, or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.
To start from scratch, move on to Build with Gradle.
To skip the basics, do the following:
git clone https://github.com/spring-guides/gs-crud-with-vaadin.git
gs-crud-with-vaadin/initial
When you’re finished, you can check your results against the code in
gs-crud-with-vaadin/complete
.Build with Gradle
First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Gradle and Maven is included here. If you’re not familiar with either, refer to Building Java Projects with Gradle or Building Java Projects with Maven.
Create the directory structure
In a project directory of your choosing, create the following subdirectory structure; for example, with
mkdir -p src/main/java/hello
on *nix systems:Create a Gradle build file
Below is the initial Gradle build file.
build.gradle
The Spring Boot gradle plugin provides many convenient features:
public static void main()
method to flag as a runnable class.Build with Maven
First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Maven is included here. If you’re not familiar with Maven, refer to Building Java Projects with Maven.
Create the directory structure
In a project directory of your choosing, create the following subdirectory structure; for example, with
mkdir -p src/main/java/hello
on *nix systems:pom.xml
The Spring Boot Maven plugin provides many convenient features:
public static void main()
method to flag as a runnable class.Build with your IDE
Create the backend services
This example is a continuation from Accessing Data with JPA. The only difference is that the entity class has getters and setters and the custom search method in the repository is a bit more graceful for end users. You don’t have to read that guide to walk through this one, but you can if you wish.
If you started with a fresh project, then add the following entity and repository objects and you’re good to go. In case you started with from the "initial" step, these are already available for you.
src/main/java/hello/Customer.java
src/main/java/hello/CustomerRepository.java
You can leave the Spring Boot based application intact as it will fill our DB with some example data.
src/main/java/hello/Application.java
Vaadin dependencies
If you checked out the "initial" state project, you have all necessary dependencies already set up, but lets look at what you need to do to add Vaadin support to a fresh Spring project. Vaadin Spring integration contains a Spring boot starter dependency collection, so all you must do is to add this Maven snippet or a similar Gradle configuration:
The example uses a newer version of Vaadin, than the default one brought in by the starter module. To use a newer version, define the Vaadin Bill of Materials (BOM) like this:
Gradle doesn’t support "BOMs" by default, but there is a handy plugin for that. Check out the build.gradle build file for an example on how to accomplish the same thing.
Define the MainView class
The MainView class is the entry point for Vaadin’s UI logic. In Spring Boot applications you just need to annotate it with
@Route
and it will be automatically picked up by Spring and shown at the root of your web app. You can customize the URL where the view is shown by giving a parameter to the Route annotation. A simple "hello world" could look like this:List entities in a data grid
For a nice layout, use the
Grid
component. The list of entities from a constructor-injectedCustomerRepository
can simply be passed to the Grid using the setItems method. The body of yourMainView
would expand like this:Although Vaadin Grid lazy load the data from the server to the browser, this solution above keeps the whole list of data in the server memory. To save some memory, you could show only the topmost results, use paging or provide a lazy loading data provider using the
setDataProvider(DataProvider)
method.Filtering the data
Before the large data set becomes a problem to your server, it will cause a headache for your users trying to find the relevant row he or she wants to edit. Use a
TextField
component to create a filter entry. First, modify thelistCustomer()
method to support filtering:Hook a listener to the
TextField
component and plug its value into that filter method. TheValueChangeListener
is called automatically during typing as we define theValueChangeMode.EAGER
to the filter text field.Define the editor component
As Vaadin UIs are just plain Java code, there is no excuse to not write re-usable code from the beginning. Define an editor component for your Customer entity. You’ll make it a Spring-managed bean so you can directly inject the
CustomerRepository
to the editor and tackle the C, U, and D parts or our CRUD functionality.src/main/java/hello/CustomerEditor.java
In a larger application you could then use this editor component in multiple places. Also note, that in large applications, you might want to apply some common patterns like MVP to structure your UI code (which is outside the scope of this guide).
Wire the editor
In the previous steps you have already seen some basics of component-based programming. Using a
Button
and selection listener toGrid
, you can fully integrate our editor to the main view. The final version of the MainView class looks like this:src/main/java/hello/MainView.java
Summary
Congratulations! You’ve written a full featured CRUD UI application using Spring Data JPA for persistence. And you did it without exposing any REST services or having to write a single line of JavaScript or HTML.
See Also
The following guides may also be helpful:
The text was updated successfully, but these errors were encountered: