Docs

Documentation versions (currently viewingVaadin 25.2 (pre-release))

CRUD Form Editor using Signals

Building an form editor CRUD interface for creating and updating data in a grid 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.

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.

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

Implementation Steps

1. Create the Item Grid and Form

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

Source code
CrudEditorExample.java

Set up a grid to display items and manage selection:

Source code
CrudEditorExample.java

Create and bind the form fields using the binder:

Source code
CrudEditorExample.java

2. Synchronize Grid Selection and the Form using a Signal

Use ValueSignal to track the currently selected item:

Source code
CrudEditorExample.java

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:

Source code
CrudEditorExample.java

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

Source code
CrudEditorExample.java
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:

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.

Source code
CrudEditorExample.java

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

Source code
CrudEditorExample.java

Combine all components and add them to your view:

Source code
CrudEditorExample.java

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.