Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Формы и 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").

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.