Docs

Grid

The Flow Grid component can be configured to allow inline editing. Editing can be either buffered or non-buffered. Buffered means changes must be explicitly committed, while non-buffered means it automatically commits changes on blur — that is to say, when a field loses focus.

Buffered

To see how buffered inline editing works, click on the Edit button for one of the rows in the example here. The styling for the row changes, the Edit button changes to Save, and a red X appears next to it for canceling any changes. Make a change to some of the data and click Save to see how it works. Next, test the Cancel button — the red X.

Open in a
new tab
Grid<Person> grid = new Grid<>(Person.class, false);
Editor<Person> editor = grid.getEditor();

Grid.Column<Person> firstNameColumn = grid
        .addColumn(Person::getFirstName).setHeader("First name")
        .setWidth("120px").setFlexGrow(0);
Grid.Column<Person> lastNameColumn = grid.addColumn(Person::getLastName)
        .setHeader("Last name").setWidth("120px").setFlexGrow(0);
Grid.Column<Person> emailColumn = grid.addColumn(Person::getEmail)
        .setHeader("Email");
Grid.Column<Person> editColumn = grid.addComponentColumn(person -> {
    Button editButton = new Button("Edit");
    editButton.addClickListener(e -> {
        if (editor.isOpen())
            editor.cancel();
        grid.getEditor().editItem(person);
    });
    return editButton;
}).setWidth("150px").setFlexGrow(0);

Binder<Person> binder = new Binder<>(Person.class);
editor.setBinder(binder);
editor.setBuffered(true);

TextField firstNameField = new TextField();
firstNameField.setWidthFull();
binder.forField(firstNameField)
        .asRequired("First name must not be empty")
        .withStatusLabel(firstNameValidationMessage)
        .bind(Person::getFirstName, Person::setFirstName);
firstNameColumn.setEditorComponent(firstNameField);

TextField lastNameField = new TextField();
lastNameField.setWidthFull();
binder.forField(lastNameField).asRequired("Last name must not be empty")
        .withStatusLabel(lastNameValidationMessage)
        .bind(Person::getLastName, Person::setLastName);
lastNameColumn.setEditorComponent(lastNameField);

EmailField emailField = new EmailField();
emailField.setWidthFull();
binder.forField(emailField).asRequired("Email must not be empty")
        .withValidator(
                new EmailValidator("Enter a valid email address"))
        .withStatusLabel(emailValidationMessage)
        .bind(Person::getEmail, Person::setEmail);
emailColumn.setEditorComponent(emailField);

Button saveButton = new Button("Save", e -> editor.save());
Button cancelButton = new Button(VaadinIcon.CLOSE.create(),
        e -> editor.cancel());
cancelButton.addThemeVariants(ButtonVariant.LUMO_ICON,
        ButtonVariant.LUMO_ERROR);
HorizontalLayout actions = new HorizontalLayout(saveButton,
        cancelButton);
actions.setPadding(false);
editColumn.setEditorComponent(actions);

Non-Buffered

To see how non-buffered works, double-click a row in the example below to start editing it. The styling for the row changes slightly to indicate it’s in edit mode. Press Escape, or click on a different row to stop editing. Notice how the row style returns to its original setting and that changes are saved automatically.

Open in a
new tab
Grid<Person> grid = new Grid<>(Person.class, false);
Grid.Column<Person> firstNameColumn = grid
        .addColumn(Person::getFirstName).setHeader("First name")
        .setWidth("120px").setFlexGrow(0);
Grid.Column<Person> lastNameColumn = grid.addColumn(Person::getLastName)
        .setHeader("Last name").setWidth("120px").setFlexGrow(0);
Grid.Column<Person> emailColumn = grid.addColumn(Person::getEmail)
        .setHeader("Email");

Binder<Person> binder = new Binder<>(Person.class);
Editor<Person> editor = grid.getEditor();
editor.setBinder(binder);

TextField firstNameField = new TextField();
firstNameField.setWidthFull();
addCloseHandler(firstNameField, editor);
binder.forField(firstNameField)
        .asRequired("First name must not be empty")
        .withStatusLabel(firstNameValidationMessage)
        .bind(Person::getFirstName, Person::setFirstName);
firstNameColumn.setEditorComponent(firstNameField);

TextField lastNameField = new TextField();
lastNameField.setWidthFull();
addCloseHandler(lastNameField, editor);
binder.forField(lastNameField).asRequired("Last name must not be empty")
        .withStatusLabel(lastNameValidationMessage)
        .bind(Person::getLastName, Person::setLastName);
lastNameColumn.setEditorComponent(lastNameField);

EmailField emailField = new EmailField();
emailField.setWidthFull();
addCloseHandler(emailField, editor);
binder.forField(emailField).asRequired("Email must not be empty")
        .withValidator(
                new EmailValidator("Enter a valid email address"))
        .withStatusLabel(emailValidationMessage)
        .bind(Person::getEmail, Person::setEmail);
emailColumn.setEditorComponent(emailField);

grid.addItemDoubleClickListener(e -> {
    editor.editItem(e.getItem());
    Component editorComponent = e.getColumn().getEditorComponent();
    if (editorComponent instanceof Focusable) {
        ((Focusable) editorComponent).focus();
    }
});

As an alternative, use Grid Pro for more streamlined inline editing, or CRUD for editing in a separate side panel or dialog.