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
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
Constraints defined for properties in the bean will work in the same way as if configured when the binding is created.
Java
public class Person {
@Max(2000)
private int yearOfBirth;
//Non-standard constraint provided by Hibernate Validator
@NotEmpty
private String name;
// + other fields, constructors, setters, and getters
}
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
Label formStatusLabel = new Label();
binder.setStatusLabel(formStatusLabel);
// Continue by binding fields
We can also define our own status handler to provide a custom way of handling statuses.
Java
BinderValidationStatusHandler<Person> defaultHandler = binder
.getValidationStatusHandler();
binder.setValidationStatusHandler(status -> {
// create an error message on failed bean level validations
List<ValidationResult> errors = status
.getBeanValidationErrors();
// collect all bean level error messages into a single string,
// separating each message with a <br> tag
String errorMessage = errors.stream()
.map(ValidationResult::getErrorMessage)
// sanitize the individual error strings to avoid code
// injection
// since we are displaying the resulting string as HTML
Whitelist.simpleText()))
// finally, display all bean level validation errors in a single
// label
formStatusLabel.getElement().setProperty("innerHTML", errorMessage);
setVisible(formStatusLabel, !errorMessage.isEmpty());
// Let the default handler show messages for each field
defaultHandler.statusChange(status);
});
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
binder.forField(firstName)
.bind(Person::getFirstName, Person::setFirstName);
binder.forField(lastName)
.bind(Person::getLastName, Person::setLastName);
binder.forField(gender)
.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
@PropertyId("sex")
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