Loading From and Saving To Business Objects

Once your bindings are set up, you are ready to fill the bound UI components with data from your business objects.

Changes can be written to business objects automatically or manually.

Reading and Writing Automatically

Writing to business objects automatically when the user makes changes in the UI is ususally the most convenient option.

You can bind the values directly to an instance, by allowing Binder to automatically save values from the fields.

Example: Automatically saving field values.

Binder<Person> binder = new Binder<>();

// Field binding configuration omitted,
// it should be done here

Person person = new Person("John Doe", 1957);

// Loads the values from the person instance
// Sets person to be updated when any bound field
// is updated
binder.setBean(person);

Button saveButton = new Button("Save", event -> {
    if (binder.validate().isOk()) {
        // person is always up-to-date as long as
        // there are no validation errors

        MyBackend.updatePersonInDatabase(person);
    }
});
  • The validate() call ensures that bean-level validators are checked when saving automatically.

Warning
When you use the setBean method, the business object instance updates whenever the user changes the value of a bound field. If another part of the application simultaneously uses the same instance, that part could display changes before the user saves. You can prevent this by using a copy of the edited object, or by manually writing to only update the object when the user saves.

Reading Manually

You can use the readBean method to manually read values from a business object instance into the UI components.

Example: Using the readBean method.

Person person = new Person("John Doe", 1957);

binder.readBean(person);
  • This example assumes that binder has been configured with a TextField bound to the name property.

  • The value "John Doe" displays in the field.

Validating and Writing Manually

To prevent displaying multiple errors to the user, validation errors only display after the user has edited each field and submitted (loaded) the form.

You can explicitly validate the form or attempt to save the values to a business object, even if the user has not edited a field.

Example: Explicitly validating a form.

// This will make all current validation errors visible
BinderValidationStatus<Person> status =
        binder.validate();

if (status.hasErrors()) {
   notifyValidationErrors(status.getValidationErrors());
}

Writing the field values to a business object fails if any of the bound fields contain an invalid value. You can deal with invalid values in a number of different ways:

Example: Handling a checked exception.

try {
    binder.writeBean(person);
    MyBackend.updatePersonInDatabase(person);
} catch (ValidationException e) {
    notifyValidationErrors(e.getValidationErrors());
}

Example: Checking a return value.

boolean saved = binder.writeBeanIfValid(person);
if (saved) {
    MyBackend.updatePersonInDatabase(person);
} else {
    notifyValidationErrors(binder.validate()
            .getValidationErrors());
}

Example: Adding bean-level validators.

binder.withValidator(
       p -> p.getYearOfMarriage() > p.getYearOfBirth(),
       "Marriage year must be bigger than birth year.");
  • The withValidator(Validator) method runs on the bound bean after update of the values of the bound fields.

  • Bean-level validators also run as part of writeBean(Object), writeBeanIfValid(Object) and validate(Object), if the content passes all field-level validators.

Note
For bean-level validators, the bean must be updated before the validator runs. If a bean-level validator fails in writeBean(Object) or writeBeanIfValid(Object), the bean reverts to the state it was in before returning from the method. Remember to check your getters/setters to ensure there are no unwanted side effects.

Tracking Binding Status

Binder keeps track of which bindings have been updated by the user and which bindings are in an invalid state. It fires an event when there are status changes. You can use this event to appropriately enable and disable the form buttons, depending on the current status of the form.

Example: Enabling the save and reset buttons when changes are detected.

binder.addStatusChangeListener(event -> {
    boolean isValid = event.getBinder().isValid();
    boolean hasChanges = event.getBinder().hasChanges();

    saveButton.setEnabled(hasChanges && isValid);
    resetButton.setEnabled(hasChanges);
});