summaryrefslogtreecommitdiffstats
path: root/documentation/datamodel/datamodel-itembinding.asciidoc
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/datamodel/datamodel-itembinding.asciidoc')
-rw-r--r--documentation/datamodel/datamodel-itembinding.asciidoc377
1 files changed, 377 insertions, 0 deletions
diff --git a/documentation/datamodel/datamodel-itembinding.asciidoc b/documentation/datamodel/datamodel-itembinding.asciidoc
new file mode 100644
index 0000000000..fd21b72267
--- /dev/null
+++ b/documentation/datamodel/datamodel-itembinding.asciidoc
@@ -0,0 +1,377 @@
+---
+title: Creating Forms by Binding Fields to Items
+order: 4
+layout: page
+---
+
+[[datamodel.itembinding]]
+= Creating Forms by Binding Fields to Items
+
+Most applications in existence have forms of some sort. Forms contain fields,
+which you want to bind to a data source, an item in the Vaadin data model.
+[classname]#FieldGroup# provides an easy way to bind fields to the properties of
+an item. You can use it by first creating a layout with some fields, and then
+call it to bind the fields to the data source. You can also let the
+[classname]#FieldGroup# create the fields using a field factory. It can also
+handle commits. Notice that [classname]#FieldGroup# is not a user interface
+component, so you can not add it to a layout.
+
+[[datamodel.itembinding.simple]]
+== Simple Binding
+
+Let us start with a data model that has an item with a couple of properties. The
+item could be any item type, as described earlier.
+
+
+----
+// Have an item
+PropertysetItem item = new PropertysetItem();
+item.addItemProperty("name", new ObjectProperty<String>("Zaphod"));
+item.addItemProperty("age", new ObjectProperty<Integer>(42));
+----
+
+Next, you would design a form for editing the data. The [classname]#FormLayout#
+(
+<<dummy/../../../framework/layout/layout-formlayout#layout.formlayout,"FormLayout">>
+is ideal for forms, but you could use any other layout as well.
+
+
+----
+// Have some layout and create the fields
+FormLayout form = new FormLayout();
+
+TextField nameField = new TextField("Name");
+form.addComponent(nameField);
+
+TextField ageField = new TextField("Age");
+form.addComponent(ageField);
+----
+
+Then, we can bind the fields to the data as follows:
+
+
+----
+// Now create the binder and bind the fields
+FieldGroup binder = new FieldGroup(item);
+binder.bind(nameField, "name");
+binder.bind(ageField, "age");
+----
+
+The above way of binding is not different from simply calling
+[methodname]#setPropertyDataSource()# for the fields. It does, however, register
+the fields in the field group, which for example enables buffering or validation
+of the fields using the field group, as described in
+<<datamodel.itembinding.buffering>>.
+
+Next, we consider more practical uses for a [classname]#FieldGroup#.
+
+
+[[datamodel.itembinding.fieldfactory]]
+== Using a [interfacename]#FieldFactory# to Build and Bind Fields
+
+Using the [methodname]#buildAndBind()# methods, [classname]#FieldGroup# can
+create fields for you using a [interfacename]#FieldGroupFieldFactory#, but you
+still have to add them to the correct position in your layout.
+
+
+----
+// Have some layout
+FormLayout form = new FormLayout();
+
+// Now create a binder that can also create the fields
+// using the default field factory
+FieldGroup binder = new FieldGroup(item);
+form.addComponent(binder.buildAndBind("Name", "name"));
+form.addComponent(binder.buildAndBind("Age", "age"));
+----
+
+
+[[datamodel.itembinding.formclass]]
+== Binding Member Fields
+
+The [methodname]#bindMemberFields()# method in [classname]#FieldGroup# uses
+reflection to bind the properties of an item to field components that are member
+variables of a class. Hence, if you implement a form as a class with the fields
+stored as member variables, you can use this method to bind them super-easy.
+
+The item properties are mapped to the members by the property ID and the name of
+the member variable. If you want to map a property with a different ID to a
+member, you can use the [literal]#++@PropertyId++# annotation for the member,
+with the property ID as the parameter.
+
+For example:
+
+
+----
+// Have an item
+PropertysetItem item = new PropertysetItem();
+item.addItemProperty("name", new ObjectProperty<String>("Zaphod"));
+item.addItemProperty("age", new ObjectProperty<Integer>(42));
+
+// Define a form as a class that extends some layout
+class MyForm extends FormLayout {
+ // Member that will bind to the "name" property
+ TextField name = new TextField("Name");
+
+ // Member that will bind to the "age" property
+ @PropertyId("age")
+ TextField ageField = new TextField("Age");
+
+ public MyForm() {
+ // Customize the layout a bit
+ setSpacing(true);
+
+ // Add the fields
+ addComponent(name);
+ addComponent(ageField);
+ }
+}
+
+// Create one
+MyForm form = new MyForm();
+
+// Now create a binder that can also creates the fields
+// using the default field factory
+FieldGroup binder = new FieldGroup(item);
+binder.bindMemberFields(form);
+
+// And the form can be used in an higher-level layout
+layout.addComponent(form);
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.itembinding.formclass.extended[on-line example, window="_blank"].
+
+[[datamodel.itembinding.formclass.customcomponent]]
+=== Encapsulating in [classname]#CustomComponent#
+
+Using a [classname]#CustomComponent# can be better for hiding the implementation
+details than extending a layout. Also, the use of the [classname]#FieldGroup#
+can be encapsulated in the form class.
+
+Consider the following as an alternative for the form implementation presented
+earlier:
+
+
+----
+// A form component that allows editing an item
+class MyForm extends CustomComponent {
+ // Member that will bind to the "name" property
+ TextField name = new TextField("Name");
+
+ // Member that will bind to the "age" property
+ @PropertyId("age")
+ TextField ageField = new TextField("Age");
+
+ public MyForm(Item item) {
+ FormLayout layout = new FormLayout();
+ layout.addComponent(name);
+ layout.addComponent(ageField);
+
+ // Now use a binder to bind the members
+ FieldGroup binder = new FieldGroup(item);
+ binder.bindMemberFields(this);
+
+ setCompositionRoot(layout);
+ }
+}
+
+// And the form can be used as a component
+layout.addComponent(new MyForm(item));
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.itembinding.formclass.customcomponent[on-line example, window="_blank"].
+
+
+
+[[datamodel.itembinding.buffering]]
+== Buffering Forms
+
+Just like for individual fields, as described in
+<<dummy/../../../framework/components/components-fields#components.fields.buffering,"Field
+Buffering">>, a [classname]#FieldGroup# can handle buffering the form content so
+that it is written to the item data source only when [methodname]#commit()# is
+called for the group. It runs validation for all fields in the group and writes
+their values to the item data source only if all fields pass the validation.
+Edits can be discarded, so that the field values are reloaded from the data
+source, by calling [methodname]#discard()#. Buffering is enabled by default, but
+can be disabled by calling [methodname]#setBuffered(false)# for the
+[classname]#FieldGroup#.
+
+
+----
+// Have an item of some sort
+final PropertysetItem item = new PropertysetItem();
+item.addItemProperty("name", new ObjectProperty<String>("Q"));
+item.addItemProperty("age", new ObjectProperty<Integer>(42));
+
+// Have some layout and create the fields
+Panel form = new Panel("Buffered Form");
+form.setContent(new FormLayout());
+
+// Build and bind the fields using the default field factory
+final FieldGroup binder = new FieldGroup(item);
+form.addComponent(binder.buildAndBind("Name", "name"));
+form.addComponent(binder.buildAndBind("Age", "age"));
+
+// Enable buffering (actually enabled by default)
+binder.setBuffered(true);
+
+// A button to commit the buffer
+form.addComponent(new Button("OK", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ try {
+ binder.commit();
+ Notification.show("Thanks!");
+ } catch (CommitException e) {
+ Notification.show("You fail!");
+ }
+ }
+}));
+
+// A button to discard the buffer
+form.addComponent(new Button("Discard", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ binder.discard();
+ Notification.show("Discarded!");
+ }
+}));
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.itembinding.formclass.customcomponent[on-line example, window="_blank"].
+
+
+[[datamodel.itembinding.beans]]
+== Binding Fields to a Bean
+
+The [classname]#BeanFieldGroup# makes it easier to bind fields to a bean. It
+also handles binding to nested beans properties. The build a field bound to a
+nested bean property, identify the property with dot notation. For example, if a
+[classname]#Person# bean has a [literal]#++address++# property with an
+[classname]#Address# type, which in turn has a [literal]#++street++# property,
+you could build a field bound to the property with
+[methodname]#buildAndBind("Street", "address.street")#.
+
+The input to fields bound to a bean can be validated using the Java Bean
+Validation API, as described in <<datamodel.itembinding.beanvalidation>>. The
+[classname]#BeanFieldGroup# automatically adds a [classname]#BeanValidator# to
+every field if a bean validation implementation is included in the classpath.
+
+
+[[datamodel.itembinding.beanvalidation]]
+== Bean Validation
+
+Vaadin allows using the Java Bean Validation API 1.0 (JSR-303) for validating
+input from fields bound to bean properties before the values are committed to
+the bean. The validation is done based on annotations on the bean properties,
+which are used for creating the actual validators automatically. See
+<<dummy/../../../framework/components/components-fields#components.fields.validation,"Field
+Validation">> for general information about validation.
+
+Using bean validation requires an implementation of the Bean Validation API,
+such as Hibernate Validator ( [filename]#hibernate-validator-4.2.0.Final.jar# or
+later) or Apache Bean Validation. The implementation JAR must be included in the
+project classpath when using the bean validation, or otherwise an internal error
+is thrown.
+
+Bean validation is especially useful when persisting entity beans with the
+Vaadin JPAContainer, described in
+<<dummy/../../../framework/jpacontainer/jpacontainer-overview.asciidoc#jpacontainer.overview,"Vaadin
+JPAContainer">>.
+
+[[datamodel.itembinding.beanvalidation.annotations]]
+=== Annotations
+
+The validation constraints are defined as annotations. For example, consider the
+following bean:
+
+
+----
+// Here is a bean
+public class Person implements Serializable {
+ @NotNull
+ @javax.validation.constraints.Size(min=2, max=10)
+ String name;
+
+ @Min(1)
+ @Max(130)
+ int age;
+
+ // ... setters and getters ...
+}
+----
+
+For a complete list of allowed constraints for different data types, please see
+the link:http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html[Bean Validation
+API documentation].
+
+
+[[datamodel.itembinding.beanvalidation.validating]]
+=== Validating the Beans
+
+Validating a bean is done with a [classname]#BeanValidator#, which you
+initialize with the name of the bean property it should validate and add it the
+the editor field.
+
+In the following example, we validate a single unbuffered field:
+
+
+----
+Person bean = new Person("Mung bean", 100);
+BeanItem<Person> item = new BeanItem<Person> (bean);
+
+// Create an editor bound to a bean field
+TextField firstName = new TextField("First Name",
+ item.getItemProperty("name"));
+
+// Add the bean validator
+firstName.addValidator(new BeanValidator(Person.class, "name"));
+
+firstName.setImmediate(true);
+layout.addComponent(firstName);
+----
+
+In this case, the validation is done immediately after focus leaves the field.
+You could do the same for the other field as well.
+
+Bean validators are automatically created when using a
+[classname]#BeanFieldGroup#.
+
+
+----
+// Have a bean
+Person bean = new Person("Mung bean", 100);
+
+// Form for editing the bean
+final BeanFieldGroup<Person> binder =
+ new BeanFieldGroup<Person>(Person.class);
+binder.setItemDataSource(bean);
+layout.addComponent(binder.buildAndBind("Name", "name"));
+layout.addComponent(binder.buildAndBind("Age", "age"));
+
+// Buffer the form content
+binder.setBuffered(true);
+layout.addComponent(new Button("OK", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ try {
+ binder.commit();
+ } catch (CommitException e) {
+ }
+ }
+}));
+----
+
+
+[[datamodel.itembinding.beanvalidation.locale]]
+=== Locale Setting for Bean Validation
+
+The validation error messages are defined in the bean validation implementation,
+in a [filename]#ValidationMessages.properties# file. The message is shown in the
+language specified with the locale setting for the form. The default language is
+English, but for example Hibernate Validator contains translations of the
+messages for a number of languages. If other languages are needed, you need to
+provide a translation of the properties file.
+
+
+
+
+