ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 01.01.2026
Просмотров: 2588
Скачиваний: 0
Binding Components to Data
You can use the same method in the Editor interface to bind a component that allows editing a particular property type to a property.
//Have a data model ObjectProperty property =
new ObjectProperty("Hello", String.class);
//Have a component that implements Viewer TextField editor = new TextField("Edit Greeting");
//Bind it to the data editor.setPropertyDataSource(property);
As all field components implement the Property interface, you can bind any component implementing the Viewer interface to any field, assuming that the viewer is able the view the object type of the field. Continuing from the above example, we can bind a Label to the TextField value:
Label viewer = new Label(); viewer.setPropertyDataSource(editor);
//The value shown in the viewer is updated immediately
//after editing the value in the editor (once it
//loses the focus)
editor.setImmediate(true);
9.2.2. ObjectProperty Implementation
The ObjectProperty class is a simple implementation of the Property interface that allows storing an arbitrary Java object.
// Have a component that implements Viewer interface final TextField tf = new TextField("Name");
//Have a data model with some data String myObject = "Hello";
//Wrap it in an ObjectProperty ObjectProperty property =
new ObjectProperty(myObject, String.class);
//Bind the property to the component tf.setPropertyDataSource(property);
9.2.3.Converting Between Property Type and Representation
Fields allow editing a certain type, such as a String or Date. The bound property, on the other hand, could have some entirely different type. Conversion between a representation edited by the field and the model defined in the property is handler with a converter that implements the Converter interface.
Most common type conversions, such as between string and integer, are handled by the default converters. They are created in a converter factory global in the application.
Basic Use of Converters
The setConverter(Converter) method sets the converter for a field. The method is defined in AbstractField.
// Have an integer property
final ObjectProperty<Integer> property = new ObjectProperty<Integer>(42);
ObjectProperty Implementation |
243 |
Binding Components to Data
//Create a TextField, which edits Strings final TextField tf = new TextField("Name");
//Use a converter between String and Integer tf.setConverter(new StringToIntegerConverter());
//And bind the field tf.setPropertyDataSource(property);
The built-in converters are the following:
Table 9.1. Built-in Converters
StringToIntegerConverter |
String |
Integer |
StringToDoubleConverter |
String |
Double |
StringToNumberConverter |
String |
Number |
StringToBooleanConverter |
String |
Boolean |
StringToDateConverter |
String |
Date |
DateToLongConverter |
Date |
Long |
In addition, there is a ReverseConverter that takes a converter as a parameter and reverses the conversion direction.
If a converter already exists for a type, the setConverter(Class) retrieves the converter for the given type from the converter factory, and then sets it for the field.This method is used implicitly when binding field to a property data source.
Implementing a Converter
A conversion always occurs between a representation type, edited by the field component, and a model type, that is, the type of the property data source. Converters implement the Converter interface defined in the com.vaadin.data.util.converter package.
For example, let us assume that we have a simple Complex type for storing complex values.
public class ComplexConverter
implements Converter<String, Complex> { @Override
public Complex convertToModel(String value, Locale locale) throws ConversionException {
String parts[] =
value.replaceAll("[\\(\\)]", "").split(","); if (parts.length != 2)
throw new ConversionException(
"Unable to parse String to Complex"); return new Complex(Double.parseDouble(parts[0]),
Double.parseDouble(parts[1]));
}
@Override
public String convertToPresentation(Complex value, Locale locale)
throws ConversionException {
return "("+value.getReal()+","+value.getImag()+")";
}
@Override
public Class<Complex> getModelType() { return Complex.class;
244 |
Converting Between Property Type and Representation |
Binding Components to Data
}
@Override
public Class<String> getPresentationType() { return String.class;
}
}
The conversion methods get the locale for the conversion as a parameter.
Converter Factory
If a field does not directly allow editing a property type, a default converter is attempted to create using an application-global converter factory. If you define your own converters that you wish to include in the converter factory, you need to implement one yourself. While you could implement the ConverterFactory interface, it is usually easier to just extend DefaultConverterFactory.
class MyConverterFactory extends DefaultConverterFactory { @Override
public <PRESENTATION, MODEL> Converter<PRESENTATION, MODEL> createConverter(Class<PRESENTATION> presentationType,
Class<MODEL> modelType) { // Handle one particular type conversion
if (String.class == presentationType && Complex.class == modelType)
return (Converter<PRESENTATION, MODEL>) new ComplexConverter();
// Default to the supertype
return super.createConverter(presentationType, modelType);
}
}
// Use the factory globally in the application Application.getCurrentApplication().setConverterFactory(
new MyConverterFactory());
9.2.4. Implementing the Property Interface
Implementation of the Property interface requires defining setters and getters for the value and the read-only mode. Only a getter is needed for the property type, as the type is often fixed in property implementations.
The following example shows a simple implementation of the Property interface:
class MyProperty implements Property { Integer data = 0;
boolean readOnly = false;
// Return the data type of the model public Class<?> getType() {
return Integer.class;
}
public Object getValue() { return data;
}
// Override the default implementation in Object @Override
public String toString() {
return Integer.toHexString(data);
}
Implementing the Property Interface |
245 |
Binding Components to Data
public boolean isReadOnly() { return readOnly;
}
public void setReadOnly(boolean newStatus) { readOnly = newStatus;
}
public void setValue(Object newValue)
throws ReadOnlyException, ConversionException { if (readOnly)
throw new ReadOnlyException();
//Already the same type as the internal representation if (newValue instanceof Integer)
data = (Integer) newValue;
//Conversion from a string is required
else if (newValue instanceof String) try {
data = Integer.parseInt((String) newValue, 16); } catch (NumberFormatException e) {
throw new ConversionException();
}
else
// Don't know how to convert any other types throw new ConversionException();
// Reverse decode the hexadecimal value
}
}
//Instantiate the property and set its data MyProperty property = new MyProperty(); property.setValue(42);
//Bind it to a component
final TextField tf = new TextField("Name", property);
The components get the displayed value by the toString() method, so it is necessary to override it. To allow editing the value, value returned in the toString() must be in a format that is accepted by the setValue() method, unless the property is read-only.The toString() can perform any type conversion necessary to make the internal type a string, and the setValue() must be able to make a reverse conversion.
The implementation example does not notify about changes in the property value or in the readonly mode. You should normally also implement at least the Property.ValueChangeNotifier and Property.ReadOnlyStatusChangeNotifier. See the ObjectProperty class for an example of the implementation.
9.3. Holding properties in Items
The Item interface provides access to a set of named properties. Each property is identified by a property identifier (PID) and a reference to such a property can be queried from an Item with getItemProperty() using the identifier.
Examples on the use of items include rows in a Table, with the properties corresponding to table columns, nodes in a Tree, and the the data bound to a Form, with item's properties bound to individual form fields.
246 |
Holding properties in Items |
Binding Components to Data
Items are generally equivalent to objects in the object-oriented model, but with the exception that they are configurable and provide an event handling mechanism. The simplest way to utilize Item interface is to use existing implementations. Provided utility classes include a configurable property set (PropertysetItem) and a bean-to-item adapter (BeanItem). Also, a Form implements the interface and can therefore be used directly as an item.
In addition to being used indirectly by many user interface components, items provide the basic data model underlying the Form component. In simple cases, forms can even be generated automatically from items. The properties of the item correspond to the fields of the form.
The Item interface defines inner interfaces for maintaining the item property set and listening changes made to it. PropertySetChangeEvent events can be emitted by a class implementing the PropertySetChangeNotifier interface. They can be received through the PropertySetChangeListener interface.
9.3.1. The PropertysetItem Implementation
The PropertysetItem is a generic implementation of the Item interface that allows storing properties. The properties are added with addItemProperty(), which takes a name and the property as parameters.
The following example demonstrates a typical case of collecting ObjectProperty properties in an item:
PropertysetItem item = new PropertysetItem(); item.addItemProperty("name", new ObjectProperty("Zaphod")); item.addItemProperty("age", new ObjectProperty(42));
// Bind it to a component Form form = new Form(); form.setItemDataSource(item);
9.3.2. Wrapping a Bean in a BeanItem
The BeanItem implementation of the Item interface is a wrapper for Java Bean objects. In fact, only the setters and getters are required while serialization and other bean features are not, so you can wrap almost any POJOs with minimal requirements.
// Here is a bean (or more exactly a POJO) class Person {
String name; int age;
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public Integer getAge() { return age;
}
public void setAge(Integer age) { this.age = age.intValue();
}
}
// Create an instance of the bean
The PropertysetItem Implementation |
247 |
Binding Components to Data
Person bean = new Person();
// Wrap it in a BeanItem
BeanItem<Person> item = new BeanItem<Person>(bean);
// Bind it to a component Form form = new Form(); form.setItemDataSource(item);
You can use the getBean() method to get a reference to the underlying bean.
Nested Beans
You may often have composite classes where one class "has a" another class. For example, consider the following Planet class which "has a" discoverer:
// Here is a bean with two nested beans public class Planet implements Serializable {
String name; Person discoverer;
public Planet(String name, Person discoverer) { this.name = name;
this.discoverer = discoverer;
}
... getters and setters ...
}
...
// Create an instance of the bean Planet planet = new Planet("Uranus",
new Person("William Herschel", 1738));
When shown in a Form, for example, you would want to list the properties of the nested bean along the properties of the composite bean. You can do that by binding the properties of the nested bean individually with a MethodProperty or NestedMethodProperty.You should usually hide the nested bean from binding as a property by listing only the bound properties in the constructor.
//Wrap it in a BeanItem and hide the nested bean property BeanItem<Planet> item = new BeanItem<Planet>(planet,
new String[]{"name"});
//Bind the nested properties.
//Use NestedMethodProperty to bind using dot notation. item.addItemProperty("discoverername",
new NestedMethodProperty(planet, "discoverer.name"));
//The other way is to use regular MethodProperty. item.addItemProperty("discovererborn",
new MethodProperty<Person>(planet.getDiscoverer(),
"born"));
The difference is that NestedMethodProperty does not access the nested bean immediately but only when accessing the property values, while when using MethodProperty the nested bean is accessed when creating the method property. The difference is only significant if the nested bean can be null or be changed later.
You can use such a bean item for example in a Form as follows:
// Bind it to a component Form form = new Form();
248 |
Wrapping a Bean in a BeanItem |