> Markdown version of [Bind the View](https://vaadin.com/docs/latest/building-apps/views/mvvm/bind-view). Section index: [llms.txt](https://vaadin.com/docs/latest/building-apps/llms.txt)

# Bind the View

With the state and the rules in the ViewModel, the view is left with the UI components, their layout, and the bindings between them and the ViewModel. This guide builds the view of the ticket triage example.

> **Tip: Part of a Series**
>
> This is part 2 of the **Structure Views with MVVM** series. It builds on the ViewModel from [Write a ViewModel](https://vaadin.com/docs/latest/building-apps/views/mvvm/write-viewmodel.md).

## <a id="create-the-viewmodel-in-the-view"></a>Create the ViewModel in the View

The `@Route` class creates the ViewModel in its constructor, and keeps it in a `private final` field. Spring injects the application services into the view’s constructor, and the view passes them on:

TicketsView\.java

`TicketsView.java`

```java
@Route("building-apps/mvvm/tickets")
class TicketsView extends HorizontalLayout
        implements AfterNavigationObserver, BeforeLeaveObserver {

    private final TicketsViewModel viewModel;

    public TicketsView(TicketService ticketService) {
        viewModel = new TicketsViewModel(ticketService);
        setSizeFull();
        add(createList(), createEditor());
        createDiscardDialog();
    }

    @Override
    public void afterNavigation(AfterNavigationEvent event) {
        viewModel.refresh();
    }
```

The view doesn’t load data in its constructor, because Vaadin can create a view without showing it. Instead, it calls the `refresh()` command after the user has navigated to the view. See [Calling a Service on View Creation](https://vaadin.com/docs/latest/building-apps/business-logic/add-service.md#calling-a-service-on-view-creation) for more.

A field that you assign in its declaration can’t use the ViewModel, not even inside a lambda: the compiler reports that the final field might not have been initialized. Assign such fields, for example event listeners, in the constructor after the ViewModel.

## <a id="bind-ui-components-to-signals"></a>Bind UI Components to Signals

The view binds each UI component to the ViewModel once, instead of pushing values into the UI components whenever something changes. Here is the list part of the view:

`TicketsView.java`

```java
private VerticalLayout createList() {
    TextField search = new TextField("Search");
    search.setValueChangeMode(ValueChangeMode.LAZY);
    search.bindValue(viewModel.searchText(), viewModel::search);

    ComboBox<Status> statusFilter = new ComboBox<>("Status",
            Status.values());
    statusFilter.setClearButtonVisible(true);
    statusFilter.bindValue(viewModel.statusFilter(),
            viewModel::filterByStatus);

    Grid<Ticket> grid = new Grid<>();
    grid.addColumn(Ticket::title).setHeader("Title");
    grid.addColumn(Ticket::status).setHeader("Status");
    grid.addColumn(Ticket::assignee).setHeader("Assignee");
    bindItems(grid, viewModel.tickets()).setIdentifierProvider(Ticket::id);
    grid.asSingleSelect().bindValue(viewModel.selected(),
            viewModel::select);

    return new VerticalLayout(new HorizontalLayout(search, statusFilter),
            grid);
}
```

A field binds to a read-only signal and a command with `bindValue(viewModel.x(), viewModel::setX)`. The field shows the signal’s value, and passes the user’s input to the command. The grid’s selection binds the same way, through `asSingleSelect()` or `asMultiSelect()`. For the other properties of a UI component, use the one-way bindings, such as `bindText()`, `bindVisible()`, `bindEnabled()`, and `bindReadOnly()`, and render a list of UI components with `bindChildren()`. [Component Bindings](https://vaadin.com/docs/latest/flow/ui-state/building-ui.md) lists them all.

Vaadin doesn’t have bind methods for a grid’s items or a field’s error message. For those, the view uses `bindItems()` and `bindError()` from a small helper class in the application, which the [last section of this guide](#bind-grids-and-error-messages) shows. For anything else that has no bind method, write an effect with `Signal.effect()`, as the view does to open a dialog.

The editor part of the view works the same way:

`TicketsView.java`

```java
private VerticalLayout createEditor() {
    TextField title = new TextField("Title");
    title.setValueChangeMode(ValueChangeMode.EAGER);
    title.bindValue(viewModel.title(), viewModel::setTitle);
    bindError(title, viewModel.titleError());

    ComboBox<Status> status = new ComboBox<>("Status", Status.values());
    status.bindValue(viewModel.status(), viewModel::setStatus);

    TextField assignee = new TextField("Assignee");
    assignee.setValueChangeMode(ValueChangeMode.EAGER);
    assignee.bindValue(viewModel.assignee(), viewModel::setAssignee);

    Button save = new Button("Save", event -> {
        if (viewModel.save()) {
            Notification.show("Ticket saved");
        }
    });
    save.addThemeVariants(ButtonVariant.PRIMARY);
    save.bindEnabled(viewModel.canSave());

    Button discard = new Button("Discard",
            event -> viewModel.discardChanges());
    discard.bindEnabled(viewModel.dirty());

    VerticalLayout editor = new VerticalLayout(
            new FormLayout(title, status, assignee),
            new HorizontalLayout(save, discard));
    editor.bindVisible(viewModel.editing());
    return editor;
}
```

By default, a text field passes its value to the ViewModel only when the user presses `Enter` or moves the focus elsewhere. The title field uses the eager value change mode, so that the ViewModel sees every keystroke and enables **Save** as soon as the user has typed a change. The search field uses the lazy mode, which waits until the user stops typing. The [Text Field](https://vaadin.com/docs/latest/components/text-field.md) page lists the value change modes.

## <a id="call-commands-from-listeners"></a>Call Commands from Listeners

A listener calls one command, and at most acts on its result. It doesn’t call services, and doesn’t decide anything that the ViewModel could decide. The **Save** button calls `save()`, and shows a notification only when the command reports that it saved the ticket. The **Discard** button calls `discardChanges()` without checking anything first.

Sometimes a framework callback needs an answer at once, and can’t wait for a signal to change. For example, `beforeLeave()` has to decide whether to postpone the navigation. In such a callback, the view reads a signal of the ViewModel with `peek()`, such as `viewModel.dirty().peek()`. [Protect Unsaved Changes](https://vaadin.com/docs/latest/building-apps/views/mvvm/dialogs-and-navigation.md#protect-unsaved-changes) shows the whole callback.

## <a id="leave-ui-work-to-the-view"></a>Leave UI Work to the View

The view does what only a UI can do: it shows notifications and dialogs, navigates to other views, moves the focus, and reads route parameters. The ViewModel decides *that* something happens, by returning an outcome or by changing its state. The view decides *how* it shows up on the screen.

## <a id="pass-state-to-child-components"></a>Pass State to Child Components

A large view is built from child UI components, such as a list and an editor, as [Compose with Components](https://vaadin.com/docs/latest/building-apps/ui-basics/compose.md) describes. A ViewModel doesn’t change how you split the view into components. It changes where the state lives: in the ViewModel, instead of in the view.

A one-off child component, which exists only inside this view, receives its state from the view. Pass it either the whole ViewModel, or only the signals and commands that it needs:

```java
class TicketGrid extends VerticalLayout {

    TicketGrid(Signal<List<Ticket>> tickets, Signal<Ticket> selected,
            SerializableConsumer<Ticket> select) {
        Grid<Ticket> grid = new Grid<>();
        grid.addColumn(Ticket::title).setHeader("Title");
        bindItems(grid, tickets).setIdentifierProvider(Ticket::id);
        grid.asSingleSelect().bindValue(selected, select);
        add(grid);
    }
}
```

```java
// In the view
add(new TicketGrid(viewModel.tickets(), viewModel.selected(),
        viewModel::select), new TicketEditor(viewModel));
```

The two children don’t know about each other, and the view doesn’t pass anything from one to the other. The grid calls `select()`, and the editor follows the state of the ViewModel. Without a ViewModel, the view would listen to the grid, and call a setter on the editor.

A reusable component, such as a card that shows a person in many views, knows nothing about the ViewModel. It accepts generic values, and reports the user’s actions with events or callbacks, which the parent passes on to commands:

```java
master.addEmployeeSelectedListener(
        event -> viewModel.select(event.getEmployee()));
```

To show state from a signal, a reusable component takes a read-only `Signal` rather than a `ValueSignal`, or offers a bind method next to its setter. The `SignalPropertySupport` helper makes such a method easy to write; see [SignalPropertySupport Helper](https://vaadin.com/docs/latest/flow/ui-state/element-bindings.md#signalpropertysupport-helper). The component then works both in plain views and in views with a ViewModel.

Only the route view creates a ViewModel. If the ViewModel has child ViewModels, the parent ViewModel creates them, as [Split a Growing ViewModel](https://vaadin.com/docs/latest/building-apps/views/mvvm/write-viewmodel.md#split-a-growing-viewmodel) describes.

## <a id="bind-grids-and-error-messages"></a>Bind Grids & Error Messages

Vaadin doesn’t have bind methods for a grid’s items, or for a field’s error message ([flow#26829](https://github.com/vaadin/flow/issues/26829)). Copy the following class into your application, and import its methods statically in each view that has a ViewModel:

SignalBindings.java

`SignalBindings.java`

```java
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasValidation;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridMultiSelectionModel;
import com.vaadin.flow.component.grid.GridSelectionColumn;
import com.vaadin.flow.component.grid.dataview.GridLazyDataView;
import com.vaadin.flow.component.grid.dataview.GridListDataView;
import com.vaadin.flow.function.SerializableSupplier;
import com.vaadin.flow.signals.Signal;

/**
 * Binds ViewModel signals to components where Vaadin has no bind method. Each
 * binding is an effect owned by the component, so it ends when the component is
 * detached.
 */
public final class SignalBindings {

    /** Fetches one page of items. */
    @FunctionalInterface
    public interface PageFetch<T> extends Serializable {
        Stream<T> fetch(int offset, int limit);
    }

    private SignalBindings() {
    }

    /**
     * Shows the items of a list signal in a grid. The items are set once and
     * refreshed on every change, because setting them again would clear a bound
     * selection. In multi-select mode, set the selection mode first, so that
     * the select-all checkbox is kept right too.
     *
     * @return the data view, for example to set an identifier provider
     */
    public static <T> GridListDataView<T> bindItems(Grid<T> grid,
            Signal<List<T>> items) {
        List<T> rows = new ArrayList<>();
        GridListDataView<T> dataView = grid.setItems(rows);
        Signal.effect(grid, () -> {
            List<T> current = items.get();
            rows.clear();
            rows.addAll(current);
            dataView.refreshAll();
            updateSelectAllCheckbox(grid, rows);
        });
        if (grid.getSelectionModel() instanceof GridMultiSelectionModel<T>) {
            grid.addSelectionListener(e -> updateSelectAllCheckbox(grid, rows));
        }
        return dataView;
    }

    /**
     * Sets the select-all checkbox from the rows and the selection at the end
     * of the round trip, after Grid has set it. Grid sets it only when the
     * selection changes, so it goes stale when the rows change, and it shows an
     * empty grid with an empty selection as all selected. Remove this once Grid
     * keeps it right itself:
     * https://github.com/vaadin/flow-components/issues/10553
     */
    private static <T> void updateSelectAllCheckbox(Grid<T> grid,
            List<T> rows) {
        grid.getUI().ifPresent(ui -> ui.beforeClientResponse(grid, context -> {
            if (!(grid
                    .getSelectionModel() instanceof GridMultiSelectionModel<T> model)
                    || !model.isSelectAllCheckboxVisible()) {
                return;
            }
            long size = rows.size();
            long selected = rows.stream().filter(model::isSelected).count();
            grid.getChildren().filter(GridSelectionColumn.class::isInstance)
                    .map(GridSelectionColumn.class::cast).findFirst()
                    .ifPresent(column -> {
                        column.setSelectAllCheckboxState(
                                size > 0 && selected == size);
                        column.setSelectAllCheckboxIndeterminateState(
                                selected > 0 && selected < size);
                    });
        }));
    }

    /**
     * Shows items loaded page by page in a grid, and refreshes them whenever
     * the query signal changes.
     *
     * @return the data view, for example to set an identifier provider
     */
    public static <T> GridLazyDataView<T> bindItems(Grid<T> grid,
            Signal<?> query, PageFetch<T> fetch,
            SerializableSupplier<Integer> count) {
        GridLazyDataView<T> dataView = grid.setItems(
                page -> fetch.fetch(page.getOffset(), page.getLimit()),
                page -> count.get());
        Signal.effect(grid, () -> {
            query.get();
            dataView.refreshAll();
        });
        return dataView;
    }

    /**
     * Shows an error signal on a field; a null error means the value is valid.
     * Remove this once fields have their own:
     * https://github.com/vaadin/flow/issues/26829
     */
    public static <F extends Component & HasValidation> void bindError(F field,
            Signal<String> error) {
        field.setManualValidation(true);
        Signal.effect(field, () -> {
            String message = error.get();
            field.setInvalid(message != null);
            field.setErrorMessage(message);
        });
    }
}
```

The class has three helpers:

| Helper                                 | Binds                                                                                                                                                                                                     |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bindItems(grid, items)`               | A signal holding a list to the items of a grid                                                                                                                                                            |
| `bindItems(grid, query, fetch, count)` | Items loaded page by page to a grid, refreshed whenever the `query` signal changes; see [Load Data Page by Page](https://vaadin.com/docs/latest/building-apps/views/mvvm/lists.md#load-data-page-by-page) |
| `bindError(field, error)`              | A signal holding an error message to a field, where `null` means that the value is valid                                                                                                                  |

Each helper is an effect owned by the UI component, so the binding ends when the UI component is detached. Bind each grid and field with one line:

```java
bindItems(grid, viewModel.tickets()).setIdentifierProvider(Ticket::id);
bindError(title, viewModel.titleError());
```

Keep the following in mind when you use the helpers:

- Data View

  Each `bindItems()` returns the grid’s data view, as `setItems()` does. Set an identifier provider on it when a save replaces an item with a new record, as in the example. The grid then recognizes the saved ticket as the selected row.

- Selection

  Bind the selection separately, with `grid.asSingleSelect().bindValue(…​)` or `grid.asMultiSelect().bindValue(…​)`. The helpers set the items once, and refresh them on every change. Calling `setItems()` again, for example from an effect, would replace the data provider. That always clears the selection, and the selection binding passes the cleared selection to the ViewModel as `null` ([flow-components#10575](https://github.com/vaadin/flow-components/issues/10575)).

- Select-All Checkbox

  In multi-select mode, a grid updates the select-all checkbox in its header only when the selection changes, and treats an empty grid as having all its rows selected. When the rows change, the checkbox would be left in the wrong state ([flow-components#10553](https://github.com/vaadin/flow-components/issues/10553)). The in-memory `bindItems()` updates the checkbox from the rows and the selection. Call `setSelectionMode(SelectionMode.MULTI)` before `bindItems()`, so that the helper sees the mode.

- Customizing

  When a grid needs something that the helpers don’t do, such as a configurable filter or an estimated count, write the effect yourself, starting from the helper’s code.

- Grids Only

  The item helpers take a `Grid` on purpose, because other UI components react to changed items in their own ways. See [Change the Options of a Select or Combo Box](https://vaadin.com/docs/latest/building-apps/views/mvvm/forms.md#change-the-options-of-a-select-or-combo-box).

Next, [test the ViewModel](https://vaadin.com/docs/latest/building-apps/views/mvvm/test-viewmodel.md).
