Validating form data
Validation keeps wrong data from being saved: an empty title, a text that is too long, a date in the wrong format. In Vaadin you can write rules in two places: as annotations on the entity — then they work both in the form and when saving to the database — or right in the form with withValidator, if a rule concerns only that screen.
The dependency
Validation annotations are the Jakarta Validation standard, implemented by Hibernate Validator. Add to pom.xml:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>Rules on the entity
// src/main/java/com/example/blog/Post.java (fields with rules)
@NotBlank
@Size(max = 120)
private String title;
@NotBlank
private String author;
@Size(max = 2000)
private String content;
@NotNull
private LocalDate publishedOn;The imports come from the jakarta.validation.constraints package: NotBlank, NotNull, Size. The rest of the class stays the same.
| Annotation | Rule |
|---|---|
@NotNull | A value is set |
@NotBlank | The string is not empty and not just spaces |
@Size(min, max) | Length of a string or size of a list |
@Min, @Max | Number limits |
@Email | Looks like an email address |
@Past, @Future | A date in the past or the future |
BeanValidationBinder
BeanValidationBinder is a Binder that reads the annotations itself. In the form from the previous lesson, replace the binding:
// src/main/java/com/example/blog/views/PostFormView.java (fragment)
private final BeanValidationBinder<Post> binder = new BeanValidationBinder<>(Post.class);
public PostFormView(PostService service) {
binder.bindInstanceFields(this);
// the rest as before
}The import is com.vaadin.flow.data.binder.BeanValidationBinder. The forField(...) lines for the fields are no longer needed. Binder adds the required marker by itself only to fields with @NotNull, @NotEmpty or @Size(min = 1); for @NotBlank call title.setRequiredIndicatorVisible(true) after binding. Click "Save" on an empty form — Hibernate Validator messages in the browser's language appear under the title and author, such as "must not be blank".
A custom rule in the form
If a rule is needed only on this screen, add it with withValidator before bindInstanceFields:
// src/main/java/com/example/blog/views/PostFormView.java (part of the constructor)
binder.forField(title)
.withValidator(value -> !value.toLowerCase().contains("todo"), "Remove TODO from the title")
.bind("title");
binder.bindInstanceFields(this);Type the title "TODO write intro" and click "Save" — "Remove TODO from the title" appears under the field. bindInstanceFields skips fields already bound by hand, and the annotations on title are still checked.
Your own error text
You can set the message right in the annotation:
// src/main/java/com/example/blog/Post.java (fragment)
@NotBlank(message = "Enter a title")
@Size(max = 120, message = "At most 120 characters")
private String title;When validation runs
| Moment | What happens |
|---|---|
| The user leaves a field | The field is checked and highlighted |
writeBeanIfValid | All fields are checked; on error the object does not change |
binder.validate() | A manual check that returns the list of errors |
The server validates the data in any case: a forged request from the browser cannot bypass the rules.
Common mistake
BeanValidationBinder is used, but the spring-boot-starter-validation dependency is missing. The view fails with the message:
BeanValidationBinder cannot be used because a JSR-303 Bean Validation implementation not found on the classpath or could not be initialized.Add the dependency from the beginning of the lesson and restart the application.