Binding Data to Forms
In many applications users provide structured data by completing fields in forms. This data is typically represented in code as an instance of a business object (JavaBean), for example a Person in an HR application.
The Binder
class allows you to define how the values in a business object are bound to fields in the UI.
Binder
reads the values in the business object and converts them from the format expected by the business object to the format expected by the field, and vice versa.
Binder
can only bind components that implement the HasValue
interface, for example TextField
and ComboBox
.
It is also possible to validate user input and present the validation status to the user in different ways.
How to Bind Form Data
The following steps include everything needed to load, edit and save values for a form. Java 8 method references are used.
To bind data to a form:
Create a
Binder
and bind the input fields.NoteThere can only be one Binder
instance for each form. You should use it for all fields in the form.Java
Binder<Person> binder = new Binder<>(Person.class);
TextField titleField = new TextField();
// Start by defining the Field instance to use
binder.forField(titleField)
// Finalize by doing the actual binding
// to the Person class
.bind(
// Callback that loads the title
// from a person instance
Person::getTitle,
// Callback that saves the title
// in a person instance
Person::setTitle);
TextField nameField = new TextField();
// Shorthand for cases without extra configuration
binder.bind(nameField, Person::getName,
Person::setName);
Use the
Binder
to:Load values from a
person
into the field.Allow the user to edit the values.
Save the values back into a
person
instance.Java
// The person to edit
// Would be loaded from the backend
// in a real application
Person person = new Person("John Doe", 1957);
// Updates the value in each bound field component
binder.readBean(person);
Button saveButton = new Button("Save",
event -> {
try {
binder.writeBean(person);
// A real application would also save
// the updated person
// using the application's backend
} catch (ValidationException e) {
notifyValidationException(e);
}
});
// Updates the fields again with the
// previously saved values
Button resetButton = new Button("Reset",
event -> binder.readBean(person));
Every time
writeBean
is called, the data is validated and then copied from the UI to the business object.If the data is invalid, a
ValidationException
that includes all errors in the data, is thrown. This is the reasonwriteBean
is in a try/catch block.
It is also possible to use a Lambda expression, instead of a method reference.
Java
// With lambda expressions
binder.bind(titleField,
person -> person.getTitle(),
(person, title) -> {
person.setTitle(title);
logger.info("setTitle: {}", title);
});
Binding Read-only Data
To bind a component to read-only data, you can use a null
value for the setter
.
Example: Using a null
value setter.
Java
TextField fullName = new TextField();
binder.forField(fullName)
.bind(Person::getFullName, null);
To bind components that do not implement the HasValue
interface to read-only data, you can use the ReadOnlyHasValue
helper class.
Example: Using the ReadOnlyHasValue
helper class.
Java
Label fullNameLabel = new Label();
ReadOnlyHasValue<String> fullName =
new ReadOnlyHasValue<>(
text -> fullNameLabel.setText(text));
binder.forField(fullName)
.bind(Person::getFullName, null);