Java

If you want to use JSR 303 Bean Validation annotations such as Max, Min, Size, etc. then a BeanValidationBinder should be used. BeanValidationBinder extends Binder class so it has the same API but its implementation automatically adds a bean validator which takes care of JSR 303 constraints. Constraints defined for properties in the bean will work in the same way as if configured when the binding is created. Note that to use Bean Validation annotations you should have a JSR 303 implementation like Hibernate Validator available in your classpath. E.g. if you use Maven, the following dependency can be used.

XML

  1. <dependency>
  2. <groupId>org.hibernate</groupId>
  3. <artifactId>hibernate-validator</artifactId>
  4. <version>5.4.1.Final</version>
  5. </dependency>

Constraints defined for properties in the bean will work in the same way as if configured when the binding is created.

Java

  1. public class Person {
  2. @Max(2000)
  3. private int yearOfBirth;
  4. //Non-standard constraint provided by Hibernate Validator
  5. @NotEmpty
  6. private String name;
  7. // + other fields, constructors, setters, and getters
  8. }

Java

Constraint annotations can also be defined on the bean level instead of being defined for any specific property.

Note
When using unbuffered binding (with the setBean method), validation will be triggered automatically on all change events. If you use buffered binding (with the readBean and writeBean methods), validation is only triggered automatically while calling . You can trigger it manually at any time by calling validate() on the Binder, for instance in a ValueChange handler.

Validation errors caused by that bean level validation might not be directly associated with any field component shown in the user interface, so Binder cannot know where such messages should be displayed.

Similarly to how the withStatusLabel method can be used for defining where messages for a specific binding should be showed, we can also define a Label that is used for showing status messages that are not related to any specific field.

Java

  1. Label formStatusLabel = new Label();
  2. binder.setStatusLabel(formStatusLabel);
  3. // Continue by binding fields

We can also define our own status handler to provide a custom way of handling statuses.

Java

  1. BinderValidationStatusHandler<Person> defaultHandler = binder
  2. .getValidationStatusHandler();
  3. binder.setValidationStatusHandler(status -> {
  4. // create an error message on failed bean level validations
  5. List<ValidationResult> errors = status
  6. .getBeanValidationErrors();
  7. // collect all bean level error messages into a single string,
  8. // separating each message with a <br> tag
  9. String errorMessage = errors.stream()
  10. .map(ValidationResult::getErrorMessage)
  11. // sanitize the individual error strings to avoid code
  12. // injection
  13. // since we are displaying the resulting string as HTML
  14. Whitelist.simpleText()))
  15. // finally, display all bean level validation errors in a single
  16. // label
  17. formStatusLabel.getElement().setProperty("innerHTML", errorMessage);
  18. setVisible(formStatusLabel, !errorMessage.isEmpty());
  19. // Let the default handler show messages for each field
  20. defaultHandler.statusChange(status);
  21. });

Automatic Data Binding

In this tutorial, we will explain a way to automatically bind properties of a business object to form fields.

Usually, we define UI fields as members of a UI Java class in order to access them in different methods of the class. In such a case, binding those fields is really easy because the bindInstanceFields method can do this job for us. We simply need to pass an object of the UI Class to it and it matches fields of that object to the properties of the related business object based on their names.

This binds the firstName TextField to the “firstName” property in the item, lastName TextField to the “lastName” property and the gender ComboBox to the “gender” property.

Without this method, we would have to bind all the fields separately like the following example:

Java

  1. binder.forField(firstName)
  2. .bind(Person::getFirstName, Person::setFirstName);
  3. binder.forField(lastName)
  4. .bind(Person::getLastName, Person::setLastName);
  5. binder.forField(gender)
  6. .bind(Person::getGender, Person::setGender);

bindInstanceFields processes all Java member fields whose type extends HasValue (such as TextField) and that can be mapped to a property name. In case the field name does not match the corresponding property name in business object, we can use an annotation named @PropertyId to specify the property name. For example, if the related property of gender field in Person class is “sex”, we need to use the @PropertyId like the following:

Java

  1. @PropertyId("sex")
  2. private ComboBox<Gender> gender = new ComboBox<>("Gender");

It’s not always possible to automatically bind all the fields to their corresponding properties because the value type of the field may not match the property type and bindInstanceFields doesn’t automatically add a converter to the binding. E.g. consider an “age” field which is a TextField whose value type is String, while the type of the “age” property in Person class is Integer. In such case an IllegalStateException will be thrown when calling bindInstanceFields. To avoid this exception, the “age” field should be configured manually to specify its converter before calling the bindInstanceFields method:

Java