Docs

Documentation versions (currently viewingVaadin 7)

You are viewing documentation for an older Vaadin version. View latest documentation

Properties

The Property interface is the base of the Vaadin Data Model. It provides a standardized API for a single data value object that can be read (get) and written (set). A property is always typed, but can optionally support data type conversions. The type of a property can be any Java class. Optionally, properties can provide value change events for following their changes.

You can set the value of a property with setValue() and read with getValue().

In the following, we set and read the property value from a TextField component, which implements the Property interface to allow accessing the field value.

final TextField tf = new TextField("Name");

// Set the value
tf.setValue("The text field value");

// When the field value is edited by the user
tf.addValueChangeListener(
    new Property.ValueChangeListener() {
    public void valueChange(ValueChangeEvent event) {
        // Do something with the new value
        layout.addComponent(new Label(tf.getValue()));
    }
});

See the on-line example.

Changes in the property value usually fire a ValueChangeEvent, which can be handled with a ValueChangeListener. The event object provides reference to the property with getProperty(). Note that its getValue() method returns the value with Object type, so you need to cast it to the proper type.

Properties are in themselves unnamed. They are collected in items, which associate the properties with names: the Property Identifiers or PIDs. Items can be further contained in containers and are identified with Item Identifiers or IIDs. In the spreadsheet analogy, Property Identifiers would correspond to column names and Item Identifiers to row names. The identifiers can be arbitrary objects, but must implement the equals(Object) and hashCode() methods so that they can be used in any standard Java Collection.

The Property interface can be utilized either by implementing the interface or by using some of the built-in property implementations. Vaadin includes a Property interface implementation for arbitrary function pairs and bean properties, with the MethodProperty class, and for simple object properties, with the ObjectProperty class, as described later.

In addition to the simple components, selection components provide their current selection as the property value. In single selection mode, the property is a single item identifier, while in multiple selection mode it is a set of item identifiers. See the documentation of the selection components for further details.

Components that can be bound to a property have an internal default data source object, typically a ObjectProperty, which is described later. As all such components are viewers or editors, also described later, so you can rebind a component to any data source with setPropertyDataSource().

Property Viewers and Editors

The most important function of the Property as well as of the other data model interfaces is to connect classes implementing the interface directly to editor and viewer classes. This means connecting a data source (model) to a user interface component (views) to allow editing or viewing the data model.

A property can be bound to a component implementing the Viewer interface with setPropertyDataSource().

// Have a data model
ObjectProperty property =
    new ObjectProperty("Hello", String.class);

// Have a component that implements Viewer
Label viewer = new Label();

// Bind it to the data
viewer.setPropertyDataSource(property);

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

If a field has validators, as described in "Field Validation", the validators are executed before writing the value to the property data source, or by calling the validate() or commit() for the field.

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

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 handled 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([interfacename]#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);

// 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 1. Built-in Converters
Converter Representation Model

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([interfacename]#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;
    }

    @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
UI.getCurrent().getSession().setConverterFactory(
        new MyConverterFactory());

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

    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 read-only mode. You should normally also implement at least the Property.ValueChangeNotifier and Property.ReadOnlyStatusChangeNotifier. See the ObjectProperty class for an example of the implementation.