Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Forms and Binder

A form shows an object's fields and writes what the user entered back into it. Doing this by hand is tedious: every field needs setValue when the form opens and getValue on save. Binder is a Vaadin helper that links a form field to an object property once, and then moves values both ways and validates them by itself.

The new post form

// 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("New post")
public class PostFormView extends VerticalLayout {
 
    private final TextField title = new TextField("Title");
    private final TextField author = new TextField("Author");
    private final DatePicker publishedOn = new DatePicker("Date");
    private final Checkbox published = new Checkbox("Published");
    private final TextArea content = new TextArea("Text");
    private final Binder<Post> binder = new Binder<>(Post.class);
    private Post post = new Post();
 
    public PostFormView(PostService service) {
        binder.forField(title).asRequired("Enter a title").bind(Post::getTitle, Post::setTitle);
        binder.forField(author).asRequired("Enter the author").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("Save", event -> {
            if (binder.writeBeanIfValid(post)) {
                service.save(post);
                Notification.show("Post saved");
            }
        });
 
        FormLayout form = new FormLayout(title, author, publishedOn, published, content);
        form.setColspan(content, 2);
        add(form, save);
    }
}

Open http://localhost:8080/posts: a two-column form, with today's date already in the date field. Click "Save" with an empty title — the field turns red with "Enter a title", and nothing is saved. Fill in the title and author — "Post saved" appears, and the grid on the main page now has four rows.

How it works

MethodWhat it does
forField(field)Starts describing the binding for a field
asRequired("...")The field is required; the text is the error message
bind(getter, setter)Where to read the value from and where to write it
readBean(post)Fills the fields with values from the object
writeBeanIfValid(post)Validates the fields and, if all is fine, writes them to the object

While the user types, the post object does not change. Values reach it only on writeBeanIfValid, and only if all checks pass.

Automatic binding

If the form fields have the same names as the object's properties, you do not need to bind them one by one:

// src/main/java/com/example/blog/views/PostFormView.java (part of the constructor)
binder.bindInstanceFields(this);

Binder finds the fields title, author, publishedOn, published and content and binds them to the Post properties with the same names. Required fields are then set by annotations on the entity — the topic of the next lesson.

readBean or setBean

ApproachWhen the object changes
readBean + writeBeanIfValidOnly on the "Save" button
setBean(post)Right away on every field change

setBean suits instant settings, but for a form with a "Cancel" button readBean is better: cancelled edits never reach the object.

Common mistake

The fields in the class are named differently from the entity properties, and bindInstanceFields is called:

// src/main/java/com/example/blog/views/PostFormView.java (wrong)
private final TextField name = new TextField("Title");

The name field silently stays unbound. If no field matches at all, the view fails:

java.lang.IllegalStateException: There are no instance fields found for automatic binding

Name fields after the properties (title) or bind them by hand with forField(...).bind("title").

Thanks for reading!

Donate to the site — we work hard for you.