> Markdown version of [CRUD Form Editor](https://vaadin.com/docs/next/flow/ui-state/usage-examples/crud-editor). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# CRUD Form Editor using Signals

This guide demonstrates how to create a reactive CRUD (Create, Read, Update, Delete) form editor that synchronizes with a grid using signals. The approach eliminates complex event handling chains while maintaining robust functionality.

## <a id="the-use-case"></a>The Use Case

We want to create an interface where users can:

- View items in a grid

- Select an item for editing

- See immediate form updates based on selection

- Create new or update existing items

- Save changes with proper validation feedback

The key challenge is managing the selected item state reactively, ensuring UI components stay perfectly synchronized without manual coordination.

## <a id="architecture-overview"></a>Architecture Overview

Our solution leverages three core concepts with clean separation of concerns:

- **Signals** for reactive state management (especially for the selected item)

- **Binder** for form-to-data binding and validation

- **Effects** to synchronize data between grid selection and form

## <a id="implementation-steps"></a>Implementation Steps

### <a id="1-create-the-item-grid-and-form"></a>1. Create the Item Grid and Form

First, we need a data model class that represents our items:

`CrudEditorExample.java`

```java
static public class Item {
    private long id;
    @NonNull
    private String product = "";
    @NonNull
    private String category = "";
}
// Constructor, getters, and setters are omitted for brevity
```

Set up a grid to display items and manage selection:

`CrudEditorExample.java`

```java
Grid<Item> itemGrid = new Grid<>(Item.class);
itemGrid.setColumns("product", "category");
itemGrid.setItems(listItems());
```

Create and bind the form fields using the binder:

`CrudEditorExample.java`

```java
Binder<Item> itemBinder = new Binder<>();

TextField productNameField = new TextField("Product");
itemBinder.forField(productNameField).bind(Item::getProduct,
        Item::setProduct);

ComboBox<String> categoryField = new ComboBox<>("Category");
categoryField.setItems(List.of("Office", "Tech", "Stationery"));
itemBinder.forField(categoryField).bind(Item::getCategory,
        Item::setCategory);
```

### <a id="2-synchronize-grid-selection-and-the-form-using-a-signal"></a>2. Synchronize Grid Selection and the Form using a Signal

Use `ValueSignal` to track the currently selected item:

`CrudEditorExample.java`

```java
static private final Item NEW_ITEM = new Item();
    ValueSignal<Item> selectedItemSignal = new ValueSignal<>(NEW_ITEM);
```

This signal will hold either a real selected item (for editing) or a special `NEW_ITEM` placeholder for creating new items. The new item is the initial value, as nothing is selected by default.

Now create bidirectional synchronization between grid selection and the signal:

`CrudEditorExample.java`

```java
// Update signal when user selects an item in the grid
itemGrid.addSelectionListener((event) -> selectedItemSignal
        .set(event.getFirstSelectedItem().orElse(NEW_ITEM)));

// Keep grid selection in sync with the signal (if signal changes
// programmatically)
Signal.effect(this, () -> itemGrid.select(selectedItemSignal.get()));
```

Next, add an effect to update the binder with the selected item data:

`CrudEditorExample.java`

```java
Signal.effect(this,
        () -> itemBinder.readBean(selectedItemSignal.get()));
```

> **Note:** This example uses `Binder::readBean(bean)`, which copies the bean’s property values into the form fields. Changes in the fields are held by the binder and only written back when you explicitly call `Binder::writeBean(bean)`. This is the recommended approach when form fields bind to signals, because it gives you full control over when the signal value is updated (see the save example below).
>
> Using `Binder::setBean(bean)` instead binds the form fields directly to the bean instance. Every field edit is written to the bean immediately via its setter methods. Those direct mutations are not detected by signals, so the signal value can change without triggering reactive updates. This may suit scenarios where immediate write-through is desired, but in most cases `readBean` combined with `writeBean` is preferred.

Now create and bind individual form fields:

### <a id="3-create-a-dynamic-save-button"></a>3. Create a Dynamic Save Button

Add a derived boolean signal to distinguish between creating a new and exiting an existing item selected in the grid.

`CrudEditorExample.java`

```java
// Derived signal, true when no item is selected (creating a new item)
final Signal<Boolean> creatingItemSignal = selectedItemSignal
        .map(NEW_ITEM::equals);
```

Add the save button with reactive label and behavior depending on whether a new or existing item is being edited:

`CrudEditorExample.java`

```java
Button saveButton = new Button(
        () -> creatingItemSignal.get() ? "Create" : "Update");

saveButton.addClickListener((event) -> {
    try {
        // Note: the method uses `peek()` to read signals, as the click
        // listener should not subscribe to the state changes.
        boolean creatingItem = creatingItemSignal.peek();
        // Create a new item instance or reuse existing for editing
        Item item = creatingItem ? new Item()
                : selectedItemSignal.peek();

        // Save changes from the binder
        itemBinder.writeBean(item);
        item = saveItem(item);

        // Update the selected item signal to use the saved item
        selectedItemSignal.set(item);

        // Refresh the data in the grid
        itemGrid.getDataProvider().refreshAll();

        // Show success notification
        final String successMessage = creatingItem ? "Item added"
                : "Item updated";
        Notification
                .show(successMessage, 3000,
                        Notification.Position.BOTTOM_END)
                .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
    } catch (ValidationException e) {
        // Show validation error
        Notification.show("Invalid item", 3000,
                Notification.Position.BOTTOM_END);
    }
});
```

Combine all components and add them to your view:

`CrudEditorExample.java`

```java
FormLayout formLayout = new FormLayout();
formLayout.setAutoResponsive(true);
formLayout.addFormRow(productNameField, categoryField, saveButton);

add(formLayout);
```

## <a id="key-takeaways"></a>Key Takeaways

- **Using signals for UI state**

  The `selectedItemSignal` becomes the single source of truth for "which item is being edited". All components that need to display/edit this item can react to changes in this signal. The grid and form stay perfectly synchronized without manual coordination.

- **Adding derived signals for computed logic**

  The `creatingItemSignal` tells us whether we’re editing a new (unsaved) item or an existing one. This drives dynamic behavior like button labels and data persistence logic.

- **Using effects for UI reactivity**

  `Signal.effect()` performs reactive UI updates based on signal dependencies.

- `Binder::readBean(bean)` and `Biner::writeBean(bean)`

  Used to synchronize the form state between the binder and the bean state signal.

- **When to use `Signal::peek()`**

  When you need the current value but don’t want to create a dependency on the signal. For example, in event handlers where you just need the current state without subscribing to changes.

## <a id="related-topics"></a>Related Topics

- [Form Binding with Dynamic Validation](https://vaadin.com/docs/next/flow/ui-state/usage-examples/binder-integration.md) - Combining Binder with signals for forms with dynamic validation logic

- [Local Signals](https://vaadin.com/docs/next/flow/ui-state/local-signals.md) - Understanding ValueSignal and two-way binding

- [Effects and Computed Signals](https://vaadin.com/docs/next/flow/ui-state/effects-computed.md) - Creating derived values

- [Component Bindings](https://vaadin.com/docs/next/flow/ui-state/building-ui.md) - Binding signals to component properties

- [Grid](https://vaadin.com/docs/next/flow/components/grid.md) - Reference documentation for Grid component
