ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 01.01.2026

Просмотров: 2625

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Binding Components to Data

form.setItemDataSource(item);

// Nicer captions form.getField("discoverername").setCaption("Discoverer"); form.getField("discovererborn").setCaption("Born");

Figure 9.3. A Form with Nested Bean Properties

The BeanContainer and BeanItemContainer allow easy definition of nested bean properties with addNestedContainerProperty(), as described in the section called “Nested Properties”.

9.4. Creating Forms by Binding Fields to Items

Because of pressing release schedules to get this edition to your hands, we were unable to completely update this chapter. Some form handling is still under work, especially form validation.

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. 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 FieldGroup create the fields using a field factory. It can also handle commits. Notice that FieldGroup is not a user interface component, so you can not add it to a layout.

9.4.1. 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 FormLayout (Section 6.5, “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");

Creating Forms by Binding Fields to Items

249


Binding Components to Data

The above way of binding is not different from simply calling 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 Section 9.4.4, “Buffering Forms”.

Next, we consider more practical uses for a FieldGroup.

9.4.2. Using a FieldFactory to Build and Bind Fields

Using the buildAndBind() methods, FieldGroup can create fields for you using a 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"));

9.4.3. Binding Member Fields

The bindMemberFields() method in 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 supereasy.

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 @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

250

Using a FieldFactory to Build and Bind Fields


Binding Components to Data

//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);

Encapsulating in CustomComponent

Using a CustomComponent can be better for hiding the implementation details than extending a layout. Also, the use of the 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));

9.4.4. Buffering Forms

A FieldGroup handles buffering the form content so that it is written to the data model only when the commit() is called for the FieldGroup. Edits can be discarded, so that the underlying property value is reloaded, by calling discard(). Buffering is enabled by default, but can be set with the setBuffered() method in 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

Buffering Forms

251


Binding Components to Data

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!");

}

}));

9.4.5. Binding Fields to a Bean

The 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 Person bean has a address property with an Address type, which in turn has a street property, you could build a field bound to the property with buildAndBind("Street", "address.street").

The input to fields bound to a bean can be validated using the Java Bean Validation API, as described in Section 9.4.6, “Bean Validation”.The BeanFieldGroup automatically adds a BeanValidator to every field if a bean validation implementation is included in the classpath.

9.4.6. 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.

Using bean validation requires an implementation of the Bean Validation API, such as Hibernate Validator (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 Chapter 21, Vaadin JPAContainer.

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;

252

Binding Fields to a Bean

Binding Components to Data

// ... setters and getters ...

}

For a complete list of allowed constraints for different data types, please see the Bean Validation API documentation [http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html].

Validating the Beans

Validating a bean is done with a 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 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) {

}

}

}));

Locale Setting for Bean Validation

The validation error messages are defined in the bean validation implementation, in a 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.

Bean Validation

253