123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- ---
- 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
-
- ifdef::vaadin7[]
- 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#.
- endif::vaadin7[]
-
- ----
- // 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
- ifdef::vaadin7[]
- 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.
- endif::vaadin7[]
-
- 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.
|