Формы и Binder
Форма показывает поля объекта и записывает в него то, что ввёл пользователь. Делать это вручную утомительно: на каждое поле нужно setValue при открытии и getValue при сохранении. Binder — помощник Vaadin, который связывает поле формы со свойством объекта один раз, а потом сам переносит значения туда и обратно и проверяет их.
Форма новой статьи
// src/main/java/com/example/blog/views/PostFormView.java
package com.example.blog.views;
import com.example.blog.Post;
import com.example.blog.PostService;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import java.time.LocalDate;
@Route("posts")
@PageTitle("Новая статья")
public class PostFormView extends VerticalLayout {
private final TextField title = new TextField("Заголовок");
private final TextField author = new TextField("Автор");
private final DatePicker publishedOn = new DatePicker("Дата");
private final Checkbox published = new Checkbox("Опубликована");
private final TextArea content = new TextArea("Текст");
private final Binder<Post> binder = new Binder<>(Post.class);
private Post post = new Post();
public PostFormView(PostService service) {
binder.forField(title).asRequired("Введите заголовок").bind(Post::getTitle, Post::setTitle);
binder.forField(author).asRequired("Укажите автора").bind(Post::getAuthor, Post::setAuthor);
binder.forField(publishedOn).bind(Post::getPublishedOn, Post::setPublishedOn);
binder.forField(published).bind(Post::isPublished, Post::setPublished);
binder.forField(content).bind(Post::getContent, Post::setContent);
post.setPublishedOn(LocalDate.now());
binder.readBean(post);
Button save = new Button("Сохранить", event -> {
if (binder.writeBeanIfValid(post)) {
service.save(post);
Notification.show("Статья сохранена");
}
});
FormLayout form = new FormLayout(title, author, publishedOn, published, content);
form.setColspan(content, 2);
add(form, save);
}
}Откройте http://localhost:8080/posts: форма в две колонки, в поле даты уже стоит сегодняшнее число. Нажмите «Сохранить» с пустым заголовком — поле подсветится красным с текстом «Введите заголовок», и ничего не сохранится. Заполните заголовок и автора — появится «Статья сохранена», а на главной странице в таблице станет четыре строки.
Как это работает
| Метод | Что делает |
|---|---|
forField(поле) | Начинает описание связи для поля |
asRequired("...") | Поле обязательно, текст — сообщение об ошибке |
bind(геттер, сеттер) | Откуда читать значение и куда писать |
readBean(post) | Заполняет поля значениями из объекта |
writeBeanIfValid(post) | Проверяет поля и, если всё верно, записывает их в объект |
Пока пользователь печатает, объект post не меняется. Значения попадают в него только при writeBeanIfValid, и только если все проверки прошли.
Автоматическая привязка
Если поля формы названы так же, как свойства объекта, связи можно не писать по одной:
// src/main/java/com/example/blog/views/PostFormView.java (фрагмент конструктора)
binder.bindInstanceFields(this);Binder найдёт поля title, author, publishedOn, published, content и свяжет их со свойствами Post с теми же именами. Обязательность при этом задают аннотации на сущности — это тема следующего урока.
readBean или setBean
| Способ | Когда объект меняется |
|---|---|
readBean + writeBeanIfValid | Только по кнопке «Сохранить» |
setBean(post) | Сразу при каждом изменении поля |
setBean удобен для мгновенных настроек, но для формы с кнопкой «Отмена» лучше readBean: отменённые правки не попадут в объект.
Частая ошибка
Поля в классе названы иначе, чем свойства сущности, и вызван bindInstanceFields:
// src/main/java/com/example/blog/views/PostFormView.java (неправильно)
private final TextField name = new TextField("Заголовок");Поле name молча останется непривязанным. А если не совпало ни одно поле, экран упадёт:
java.lang.IllegalStateException: There are no instance fields found for automatic bindingНазывайте поля как свойства (title) или свяжите их вручную через forField(...).bind("title").