aboutsummaryrefslogtreecommitdiffstats
path: root/documentation/components/components-fields.asciidoc
diff options
context:
space:
mode:
authorJohannes Dahlström <johannesd@vaadin.com>2016-07-21 17:47:56 +0300
committerArtur Signell <artur@vaadin.com>2016-08-05 10:19:46 +0300
commit34eb8d4c94b69a058c5a4fe5cf503df2aee006ce (patch)
tree4f5dcb34ceba7965f2c7149da0e0002a1de5c962 /documentation/components/components-fields.asciidoc
parent80e7941da9c74c893f8f55fe4b3c71bc36da44d1 (diff)
downloadvaadin-framework-34eb8d4c94b69a058c5a4fe5cf503df2aee006ce.tar.gz
vaadin-framework-34eb8d4c94b69a058c5a4fe5cf503df2aee006ce.zip
BoV: Components/Fields: Data binding and validation using Binder
Change-Id: Iffc4a87bb907d68a1163774266401023ba7d644f
Diffstat (limited to 'documentation/components/components-fields.asciidoc')
-rw-r--r--documentation/components/components-fields.asciidoc326
1 files changed, 104 insertions, 222 deletions
diff --git a/documentation/components/components-fields.asciidoc b/documentation/components/components-fields.asciidoc
index 173544dfe7..6e8d0c9c6a 100644
--- a/documentation/components/components-fields.asciidoc
+++ b/documentation/components/components-fields.asciidoc
@@ -10,8 +10,8 @@ layout: page
((("[classname]#Field#", id="term.components.fields", range="startofrange")))
_Fields_ are components that have a value that the user can change through the
-user interface.
-<<figure.components.fields>> illustrates the inheritance relationships and the important interfaces and base classes.
+user interface. <<figure.components.fields>> illustrates the inheritance relationships
+and the important interfaces and base classes.
[[figure.components.fields]]
.Field components
@@ -69,114 +69,93 @@ guide.
The error message is set as the component error for the field and is usually
displayed in a tooltip when the mouse pointer hovers over the error indicator.
-[[components.fields.databinding]]
-== Data Binding and Conversions
-
-Fields and selects can be coupled with business data objects with the [classname]#Binder# class.
-Select components also allow management of the selectable items through the
-[classname]#DataSource# interface. [classname]#Binder# and [classname]#DataSource#
-can be thought of as bridges between the __presentation__ and __model__ architectural layers.
-
-Fields are __editors__ for values of some particular type. For example,
-[classname]#TextField# allows editing [classname]#String# values. When bound to
-a data source, the type of the source property can be something different,
-say an [classname]#Integer#. __Converters__ are used for converting the values
-between the presentation and the model. They are described in
-<<dummy/../../../framework/datamodel/datamodel-properties#datamodel.properties.converter,"Converting
-Between Model and Presentation Types">>.
-
-
[[components.fields.valuechanges]]
== Handling Field Value Changes
-[classname]#Field# inherits [classname]#Property.ValueChangeListener# to allow
-listening for field value changes and [classname]#Property.Editor# to allow
-editing values.
-
-When the value of a field changes, a [classname]#Property.ValueChangeEvent# is
-triggered for the field. You should not implement the
-[methodname]#valueChange()# method in a class inheriting
-[classname]#AbstractField#, as it is already implemented in
-[classname]#AbstractField#. You should instead implement the method explicitly
-by adding the implementing object as a listener.
+[classname]#Field# provides two methods for listening to changes to the field value:
+[methodname]#onValueChange# and [methodname]#addValueChangeListener#. The difference
+is that the former takes a [interfacename]#Consumer# object that only receives the new value;
+the latter, on the other hand, takes an [interfacename]#EventListener# that gets
+a [classname]#ValueChange# event instance containing extra information about the event.
+Both methods return a [classname]#Registration# object that can be used to later
+remove the added listener if necessary.
-[[components.fields.buffering]]
-== Field Buffering
+[source, java]
+----
+TextField textField = new TextField();
+Label echo = new Label();
+
+// Just echo in the label anything the user enters
+textField.onValueChange(echo::setValue);
+
+// Add a more complex listener
+textField.addValueChangeListener(event -> {
+ String origin = event.isUserOriginated()
+ ? "user"
+ : "application";
+ String message = origin
+ + " entered the following: "
+ + event.getValue();
+ Notification.show(message);
+});
+----
-Field components implement the [interfacename]#Buffered# and
-[interfacename]#BufferedValidatable# interfaces. When buffering is enabled for a
-field with [methodname]#setBuffered(true)#, the value is not written to the
-property data source before the [methodname]#commit()# method is called for the
-field. Calling [methodname]#commit()# also runs validators added to the field,
-and if any fail (and the [parameter]#invalidCommitted# is disabled), the value
-is not written.
+[[components.fields.databinding]]
+== Binding Fields to Data
+Fields can be grouped into _forms_ and coupled with business data objects with
+the [classname]#Binder# class. When a field is bound to a property using
+[classname]#Binder#, it gets its default value from the property, and
+is stored to the property either manually via the [methodname]#Binder.save# method,
+or automatically every time the value changes.
[source, java]
----
-form.addComponent(new Button("Commit",
- new Button.ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- try {
- editor.commit();
- } catch (InvalidValueException e) {
- Notification.show(e.getMessage());
- }
- }
-}));
-----
-See the http://demo.vaadin.com/book-examples-vaadin7/book#component.field.buffering.basic[on-line example, window="_blank"].
+class Person {
+ private String name;
+ public String getName() { /* ... */ }
+ public void setName(String) { /* ... */ }
+}
-Calling [methodname]#discard()# reads the value from the property data source to
-the current input.
+TextField nameField = new TextField();
-If the fields are bound in a [classname]#FieldGroup# that has buffering enabled,
-calling [methodname]#commit()# for the group runs validation on all fields in
-the group, and if successful, all the field values are written to the item data
-source. See
-<<dummy/../../../framework/datamodel/datamodel-itembinding#datamodel.itembinding.buffering,"Buffering
-Forms">>.
+Binder<Person> binder = new Binder<>();
+// Bind nameField to the Person.name property
+// by specifying its getter and setter
+binder.addField(nameField)
+ .bind(Person::getName, Person::setName);
-[[components.fields.validation]]
-== Field Validation
+// Bind an actual concrete Person instance.
+// After this, whenever the user changes the value
+// of nameField, p.setName is automatically called.
+Person p = new Person();
+binder.bind(p;
+----
-The input for a field component can be syntactically or semantically invalid.
-Fields implement the [interfacename]#Validatable# interface, which allows
-checking validity of the input with __validators__ that implement the
-[interfacename]#Validator# interface. You can add validators to fields with
-[methodname]#addValidator()#.
+== Validating Field Values
+User input may be syntactically or semantically invalid.
+[classname]#Binder# allows adding a chain of one or more __validators__ for
+automatically checking the validity of the input before storing it to the data
+object. You can add validators to fields by calling the [methodname]#addValidator#
+method on the [interfacename]#Binding# object returned by [methodname]#Binder.addField#.
[source, java]
----
-TextField field = new TextField("Name");
-field.addValidator(new StringLengthValidator(
- "The name must be 1-10 letters (was {0})",
- 1, 10, true));
-field.setNullRepresentation("");
-field.setNullSettingAllowed(true);
-layout.addComponent(field);
+binder.addField(nameField)
+ .addValidator(new StringLengthValidator(2, 20,
+ "Name must be between 2 and 20 characters long"))
+ .bind(Person::getName, Person::setName);
----
-See the http://demo.vaadin.com/book-examples-vaadin7/book#component.field.validation.basic[on-line example, window="_blank"].
-Failed validation is indicated with the error indicator of the field, described
-in
+Failed validation is indicated with the error indicator of the field, described in
<<dummy/../../../framework/application/application-errors#application.errors.error-indicator,"Error
-Indicator and Message">>, unless disabled with
-[methodname]#setValidationVisible(false)#. Hovering mouse on the field displays
-the error message given as a parameter for the validator. If validated
-explicitly with [methodname]#validate()#, as described later, the
-[classname]#InvalidValueException# is thrown if the validation fails, also
-carrying the error message. The value [literal]#++{0}++# in the error message
-string is replaced with the invalid input value.
-
-Validators validate the property type of the field after a possible conversion,
-not the presentation type. For example, an [classname]#IntegerRangeValidator#
-requires that the value type of the property data source is
-[classname]#Integer#.
+Indicator and Message">>. Hovering mouse on the field displays the error message
+returned by the validator. If any value in a set of bound fields fails validation,
+none of the field values are saved into the bound property until the validation
+passes.
[[components.fields.validation.builtin]]
=== Built-in Validators
@@ -184,164 +163,67 @@ requires that the value type of the property data source is
Vaadin includes the following built-in validators. The property value type is
indicated.
-[classname]#BeanValidator#::
-Validates a bean property according to annotations defined in the Bean Validation API 1.0 (JSR-303).
-This validator is usually not used explicitly, but they are created implicitly when binding fields in a [classname]#BeanFieldGroup#.
-Using bean validation requires an implementation library of the API.
-See <<dummy/../../../framework/datamodel/datamodel-itembinding#datamodel.itembinding.beanvalidation,"Bean Validation">> for details.
-
-[classname]#CompositeValidator#::
-Combines validators using logical AND and OR operators.
+[classname]#RangeValidator#: [classname]#Comparable#::
+Checks that the given [interfacename]#Comparable# value is at or between two given values.
-[classname]#DateRangeValidator#: [classname]#Date#::
-Checks that the date value is within the range at or between two given dates/times.
+[classname]#StringLengthValidator#: [classname]#String#::
+Checks that the length of the input string is at or between two given lengths.
-[classname]#DoubleRangeValidator#: [classname]#Double#::
-Checks that the double value is at or between two given values.
+[classname]#RegexpValidator#: [classname]#String#::
+Checks that the value matches the given regular expression.
[classname]#EmailValidator#: [classname]#String#::
Checks that the string value is a syntactically valid email address.
The validated syntax is close to the RFC 822 standard regarding email addresses.
-[classname]#IntegerRangeValidator#: [classname]#Integer#::
-Checks that the integer value is at or between two given values.
-
-[classname]#NullValidator#::
-Checks whether the value is or is not a null value.
-+
-For the validator to be meaningful, the component must support inputting null
-values. For example, for selection components and [classname]#TextField#,
-inputting null values can be enabled with [methodname]#setNullSettingAllowed()#.
-You also need to set the representation of null values: in selection components
-with [methodname]#setNullSelectionItemId()# and in [classname]#TextField# with
-[methodname]#setNullRepresentation()#.
-
-ifdef::web[]
-+
-Setting field as __required__ can be used for similar effect, and it also
-enables an indicator to indicate that a value is required.
-endif::web[]
-
-[classname]#RegexpValidator#: [classname]#String#::
-Checks that the value matches with the given regular expression.
-
-[classname]#StringLengthValidator#: [classname]#String#::
-Checks that the length of the input string is at or between two given lengths.
-
-ifdef::web[]
-+
-The [parameter]#allowNull# parameter determines whether null values should be
-allowed for the string, regardless of the string length. A null value has zero
-length, so it will be invalid if the minimum length is greater than zero.
-Allowing null value is meaningful only if inputting null values is enabled with
-[methodname]#setNullSettingAllowed(true)#, and typically in such case, you want
-to set the null representation to empty string with
-[methodname]#setNullRepresentation("")#. Note that _this parameter is
-deprecated_ and should normally be [parameter]#true#; then you can use
-[methodname]#setRequired()# (for the false case) or [classname]#NullValidator#.
-endif::web[]
-
-Please see the API documentation for more details.
-
-[[components.fields.validation.automatic]]
-=== Automatic Validation
-
-The validators are normally, when [literal]#++validationVisible++# is true for
-the field, executed implicitly on the next server request if the input has
-changed. If the field is in immediate mode, it (and any other fields with
-changed value) are validated immediately when the focus leaves the field.
-
-
-[source, java]
-----
-TextField field = new TextField("Name");
-field.addValidator(new StringLengthValidator(
- "The name must be 1-10 letters (was {0})",
- 1, 10, true));
-field.setImmediate(true);
-field.setNullRepresentation("");
-field.setNullSettingAllowed(true);
-layout.addComponent(field);
-----
-See the http://demo.vaadin.com/book-examples-vaadin7/book#component.field.validation.basic[on-line example, window="_blank"].
-
-
-[[components.fields.validation.explicit]]
-=== Explicit Validation
-
-The validators are executed when the [methodname]#validate()# or
-[methodname]#commit()# methods are called for the field.
+=== Implementing Custom Validators
+Validators implement the [interfacename]#Validator# interface that simply
+extends [interfacename]#java.util.function.Function#, returning a special type
+called [interfacename]#Result#. This return type represents the validation outcome:
+whether or not the given input was valid.
[source, java]
----
-// A field with automatic validation disabled
-final TextField field = new TextField("Name");
-field.setNullRepresentation("");
-field.setNullSettingAllowed(true);
-layout.addComponent(field);
-
-// Define validation as usual
-field.addValidator(new StringLengthValidator(
- "The name must be 1-10 letters (was {0})",
- 1, 10, true));
-
-// Run validation explicitly
-Button validate = new Button("Validate");
-validate.addClickListener(new ClickListener() {
+class MyValidator implements Validator<String> {
@Override
- public void buttonClick(ClickEvent event) {
- field.setValidationVisible(false);
- try {
- field.validate();
- } catch (InvalidValueException e) {
- Notification.show(e.getMessage());
- field.setValidationVisible(true);
+ public Result<String> apply(String input) {
+ if(input.length() == 6) {
+ return Result.ok(input);
+ } else {
+ return Result.error(
+ "Must be exactly six characters long");
}
}
-});
-layout.addComponent(validate);
+}
----
-See the http://demo.vaadin.com/book-examples-vaadin7/book#component.field.validation.explicit[on-line example, window="_blank"].
-
-
-[[components.fields.validation.custom]]
-=== Implementing a Custom Validator
-
-You can create custom validators by implementing the [interfacename]#Validator#
-interface and implementing its [methodname]#validate()# method. If the
-validation fails, the method should throw either
-[classname]#InvalidValueException# or [classname]#EmptyValueException#.
+Because [methodname]#Result.ok# takes the valid value as an argument, a validator
+can also do some sanitization on valid inputs, such as removing leading and
+trailing whitespace from a string. Since [interfacename]#Validator# is a functional
+interface, you can often simply write a lambda expression instead of a full class
+declaration. There is also an [methodname]#addValidator# overload that creates a
+validator from a boolean function and an error message.
[source, java]
----
-class MyValidator implements Validator {
- @Override
- public void validate(Object value)
- throws InvalidValueException {
- if (!(value instanceof String &&
- ((String)value).equals("hello")))
- throw new InvalidValueException("You're impolite");
- }
-}
+binder.addField(nameField)
+ .addValidator(name -> name.length() < 20,
+ "Name must be less than 20 characters long")
+ .bind(Person::getName, Person::setName);
-TextField field = new TextField("Say hello");
-field.addValidator(new MyValidator());
-field.setImmediate(true);
-layout.addComponent(field);
----
-See the http://demo.vaadin.com/book-examples-vaadin7/book#component.field.validation.customvalidator[on-line example, window="_blank"].
+== Converting Field Values
-[[components.fields.validation.fieldgroup]]
-=== Validation in Field Groups
+Field values are always of some particular type. For example,
+[classname]#TextField# allows editing [classname]#String# values. When bound to
+a data source, the type of the source property can be something different,
+say an [classname]#Integer#. __Converters__ are used for converting the values
+between the presentation and the model. Their usage is described in
+<<dummy/../../../framework/datamodel/datamodel-properties#datamodel.properties.converter,"Converting
+Between Model and Presentation Types">>.
-If the field is bound to a [classname]#FieldGroup#, described in
-<<dummy/../../../framework/datamodel/datamodel-itembinding#datamodel.itembinding,"Creating
-Forms by Binding Fields to Items">>, calling [methodname]#commit()# for the
-group runs the validation for all the fields in the group, and if successful,
-writes the input values to the data source.
endif::disabled[]