Component Bindings
- Overview
- Signal Basics
- Binding Text Content
- Binding Visibility
- Binding Enabled State
- Two-Way Form Field Binding
- Binding Theme Variants
- Binding Inline Styles
- Rendering Lists of Components
- Complete Example
Signals connect directly to Vaadin components through binding methods. When a signal value changes, bound components update automatically without manual listeners or state synchronization.
|
Note
|
Preview Feature
This is a preview version of Signals. You need to enable it with the feature flag |
Overview
Vaadin components provide direct binding methods for common properties. These bindings create a reactive connection between your data and the UI. This section covers the general-purpose component-level binding APIs. For lower-level Element bindings, see Element Bindings.
Signal Basics
Signals hold reactive values. The most common type is ValueSignal. For complete coverage of signal types and operations, see Local Signals and Shared Signals.
Binding Text Content
Use bindText() to bind a component’s text content to a signal:
Source code
Java
import com.vaadin.signals.local.ValueSignal;
ValueSignal<String> name = new ValueSignal<>("World");
Span greeting = new Span();
greeting.bindText(name.map(n -> "Hello, " + n + "!"));
// Displays "Hello, World!"
name.value("Vaadin");
// Automatically updates to "Hello, Vaadin!"Transforming Values
Use map() to transform signal values before display:
Source code
Java
import com.vaadin.signals.shared.SharedNumberSignal;
SharedNumberSignal counter = new SharedNumberSignal();
Button button = new Button();
button.bindText(counter.map(c -> String.format("Clicked %.0f times", c)));
// Displays "Clicked 0 times"
button.addClickListener(e -> counter.incrementBy(1));
// After click: "Clicked 1 times"Combining Multiple Signals
Use a lambda to combine values from multiple signals:
Source code
Java
ValueSignal<String> firstName = new ValueSignal<>("John");
ValueSignal<String> lastName = new ValueSignal<>("Doe");
Span fullName = new Span();
fullName.bindText(() -> firstName.value() + " " + lastName.value());
// Displays "John Doe"
firstName.value("Jane");
// Automatically updates to "Jane Doe"For expensive computations, wrap the lambda in Signal.computed() to cache the result:
Source code
Java
fullName.bindText(Signal.computed(() ->
firstName.value() + " " + lastName.value()));Binding Visibility
Use bindVisible() to show or hide components based on a boolean signal:
Source code
Java
ValueSignal<Boolean> showDetails = new ValueSignal<>(false);
Div detailsPanel = new Div();
detailsPanel.setText("Detailed information here...");
detailsPanel.bindVisible(showDetails);
// Panel is hidden initially
Button toggleButton = new Button("Show Details");
toggleButton.addClickListener(e -> showDetails.update(v -> !v));
// Clicking toggles panel visibilityConditional Visibility
Derive visibility from other signal types using map():
Source code
Java
ValueSignal<String> searchText = new ValueSignal<>("");
Span noResults = new Span("No results found");
noResults.bindVisible(searchText.map(text -> text.isEmpty()));
// Shows when search text is empty
Div results = new Div();
results.bindVisible(searchText.map(text -> !text.isEmpty()));
// Shows when search text is not emptyBinding Enabled State
Use bindEnabled() to enable or disable components based on a boolean signal:
Source code
Java
ValueSignal<Boolean> termsAccepted = new ValueSignal<>(false);
Button submitButton = new Button("Submit");
submitButton.bindEnabled(termsAccepted);
// Button is disabled initially
Checkbox termsCheckbox = new Checkbox("I accept the terms");
termsCheckbox.bindValue(termsAccepted);
// Button enables when checkbox is checkedForm Validation Example
Enable a submit button only when the form is valid:
Source code
Java
ValueSignal<String> email = new ValueSignal<>("");
ValueSignal<String> password = new ValueSignal<>("");
Signal<Boolean> formValid = Signal.computed(() -> {
String e = email.value();
String p = password.value();
return e.contains("@") && p.length() >= 8;
});
Button submitButton = new Button("Sign Up");
submitButton.bindEnabled(formValid);
// Button enables only when email contains @ and password has 8+ charactersTwo-Way Form Field Binding
Use bindValue() on form fields to create two-way binding. Changes to the field update the signal, and changes to the signal update the field:
Source code
Java
import com.vaadin.signals.shared.SharedValueSignal;
SharedValueSignal<String> username = new SharedValueSignal<>(String.class);
TextField usernameField = new TextField("Username");
usernameField.bindValue(username);
// User types "john" -> username.value() returns "john"
// username.value("jane") -> field displays "jane"Supported Field Types
Two-way binding works with all form fields implementing HasValue:
Source code
Java
// Text fields
TextField textField = new TextField();
textField.bindValue(stringSignal);
TextArea textArea = new TextArea();
textArea.bindValue(stringSignal);
// Checkbox
SharedValueSignal<Boolean> accepted = new SharedValueSignal<>(Boolean.class);
Checkbox checkbox = new Checkbox("Accept terms");
checkbox.bindValue(accepted);
// Number field
SharedValueSignal<Double> amount = new SharedValueSignal<>(Double.class);
NumberField numberField = new NumberField("Amount");
numberField.bindValue(amount);
// Select/ComboBox
SharedValueSignal<String> country = new SharedValueSignal<>(String.class);
ComboBox<String> countrySelect = new ComboBox<>("Country");
countrySelect.setItems("USA", "UK", "Germany", "France");
countrySelect.bindValue(country);
// Date picker
SharedValueSignal<LocalDate> birthDate = new SharedValueSignal<>(LocalDate.class);
DatePicker datePicker = new DatePicker("Birth date");
datePicker.bindValue(birthDate);Read-Only Binding
Use bindReadOnly() to control the read-only state of a form field:
Source code
Java
ValueSignal<Boolean> locked = new ValueSignal<>(false);
TextField field = new TextField("Document title");
field.bindReadOnly(locked);
// Field is editable initially
Button lockButton = new Button("Lock", e -> locked.value(true));
// After clicking: field becomes read-onlyBinding Theme Variants
Use getThemeList().bind() to toggle theme variants based on a signal:
Source code
Java
ValueSignal<Boolean> darkMode = new ValueSignal<>(false);
VerticalLayout layout = new VerticalLayout();
layout.getThemeList().bind("dark", darkMode);
// Dark theme applied when darkMode is true
Button toggleTheme = new Button("Toggle Dark Mode");
toggleTheme.addClickListener(e -> darkMode.update(v -> !v));Multiple Theme Variants
Bind multiple independent theme variants:
Source code
Java
ValueSignal<Boolean> compact = new ValueSignal<>(false);
ValueSignal<Boolean> rowStripes = new ValueSignal<>(true);
Grid<Person> grid = new Grid<>();
grid.getThemeList().bind("compact", compact);
grid.getThemeList().bind("row-stripes", rowStripes);Binding Inline Styles
Use getStyle().bind() to bind CSS properties to signals:
Source code
Java
ValueSignal<String> backgroundColor = new ValueSignal<>("white");
Div panel = new Div();
panel.getStyle().bind("background-color", backgroundColor);
// Change background color
backgroundColor.value("lightblue");Dynamic Styling Example
Create dynamic visual feedback based on data:
Source code
Java
SharedNumberSignal progress = new SharedNumberSignal();
Div progressBar = new Div();
progressBar.getStyle().set("height", "20px");
progressBar.getStyle().set("transition", "width 0.3s");
progressBar.getStyle().bind("width", progress.map(p -> p + "%"));
progressBar.getStyle().bind("background-color", progress.map(p ->
p < 50 ? "orange" : (p < 100 ? "blue" : "green")));Rendering Lists of Components
Use ComponentEffect.bindChildren() to efficiently render a list of components from a list signal. This method handles additions, removals, and reordering without recreating all children:
Source code
Java
import com.vaadin.signals.shared.SharedListSignal;
SharedListSignal<String> items = new SharedListSignal<>(String.class);
VerticalLayout container = new VerticalLayout();
ComponentEffect.bindChildren(container, items, itemSignal -> {
Span itemView = new Span();
itemView.bindText(itemSignal);
return itemView;
});
// Add items
items.insertLast("First item");
items.insertLast("Second item");
// Container now has two Span childrenComplex List Items
Create rich list items with nested bindings:
Source code
Java
record Todo(String text, boolean done) {
Todo withDone(boolean done) {
return new Todo(this.text, done);
}
}
SharedListSignal<Todo> todos = new SharedListSignal<>(Todo.class);
VerticalLayout todoList = new VerticalLayout();
ComponentEffect.bindChildren(todoList, todos, todoSignal -> {
HorizontalLayout row = new HorizontalLayout();
row.setAlignItems(FlexComponent.Alignment.CENTER);
// Two-way binding to the 'done' property using signal mapping
Checkbox checkbox = new Checkbox();
checkbox.bindValue(todoSignal.map(Todo::done, Todo::withDone));
Span text = new Span();
text.bindText(todoSignal.map(Todo::text));
// Strike through completed items
text.getStyle().bind("text-decoration",
todoSignal.map(t -> t.done() ? "line-through" : "none"));
Button editButton = new Button("Edit", e -> {
// Update the todo's text - this only updates the state,
// the component is not recreated
todoSignal.update(t -> new Todo(t.text() + " (edited)", t.done()));
});
Button deleteButton = new Button("Delete", e -> {
todos.remove(todoSignal);
});
row.add(checkbox, text, editButton, deleteButton);
return row;
});The map(getter, merger) method creates a two-way mapping that enables direct binding between the checkbox and the done property. See Two-Way Signal Mapping for more details.
When you update a todo item using todoSignal.update(), only the bound properties (text content, styling) are updated. The component itself is not recreated, maintaining its position and identity in the DOM.
Efficiency
The factory function runs once per list item. When you modify the list:
-
Adding items: Only new items create new components
-
Removing items: Only removed items are detached
-
Reordering: Components are moved, not recreated
-
Updating values: Item bindings update, but components aren’t recreated
Complete Example
Here’s a complete example combining multiple binding types:
Source code
Java
public class TaskManager extends VerticalLayout {
// State
private final SharedListSignal<Task> tasks = new SharedListSignal<>(Task.class);
private final ValueSignal<String> newTaskText = new ValueSignal<>("");
private final ValueSignal<Boolean> showCompleted = new ValueSignal<>(true);
// Computed signals
private final Signal<Long> totalCount = Signal.computed(() ->
(long) tasks.value().size());
private final Signal<Long> completedCount = Signal.computed(() ->
tasks.value().stream()
.filter(t -> t.value().done())
.count());
public TaskManager() {
// Header with statistics
Span stats = new Span();
stats.bindText(Signal.computed(() ->
String.format("Tasks: %d total, %d completed",
totalCount.value(), completedCount.value())));
add(stats);
// Add task form
HorizontalLayout addForm = new HorizontalLayout();
TextField taskInput = new TextField();
taskInput.setPlaceholder("Enter new task");
taskInput.bindValue(newTaskText);
Button addButton = new Button("Add Task");
addButton.bindEnabled(newTaskText.map(t -> !t.isBlank()));
addButton.addClickListener(e -> {
tasks.insertLast(new Task(newTaskText.value(), false));
newTaskText.value("");
});
addForm.add(taskInput, addButton);
add(addForm);
// Show/hide completed toggle
Checkbox showCompletedCheckbox = new Checkbox("Show completed tasks");
showCompletedCheckbox.bindValue(showCompleted);
add(showCompletedCheckbox);
// Task list
VerticalLayout taskList = new VerticalLayout();
taskList.setPadding(false);
ComponentEffect.bindChildren(taskList, tasks, taskSignal -> {
HorizontalLayout row = new HorizontalLayout();
row.setAlignItems(FlexComponent.Alignment.CENTER);
row.setWidthFull();
// Hide completed tasks when toggle is off
row.bindVisible(Signal.computed(() ->
showCompleted.value() || !taskSignal.value().done()));
// Two-way binding to the 'done' property
Checkbox doneCheckbox = new Checkbox();
doneCheckbox.bindValue(taskSignal.map(Task::done, Task::withDone));
Span taskText = new Span();
taskText.bindText(taskSignal.map(Task::text));
taskText.getStyle().bind("text-decoration",
taskSignal.map(t -> t.done() ? "line-through" : "none"));
Button deleteButton = new Button(VaadinIcon.TRASH.create());
deleteButton.addClickListener(e -> tasks.remove(taskSignal));
row.add(doneCheckbox, taskText, deleteButton);
return row;
});
add(taskList);
}
record Task(String text, boolean done) {
Task withDone(boolean done) {
return new Task(this.text, done);
}
}
}This example demonstrates:
-
Two-way binding with form fields (
bindValue()) -
Two-way property mapping with
map(getter, merger)(see Two-Way Signal Mapping) -
Conditional button enabling (
bindEnabled()) -
Computed signals for derived values
-
List rendering with
ComponentEffect.bindChildren() -
Dynamic visibility (
bindVisible()) -
Dynamic styling (
getStyle().bind())