--- title: Properties order: 2 layout: page --- [[datamodel.properties]] = Properties The [interfacename]#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 [methodname]#setValue()# and read with [methodname]#getValue()#. In the following, we set and read the property value from a [classname]#TextField# component, which implements the [interfacename]#Property# interface to allow accessing the field value. [source, java] ---- 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 http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.properties.basic[on-line example, window="_blank"]. Changes in the property value usually fire a [classname]#ValueChangeEvent#, which can be handled with a [classname]#ValueChangeListener#. The event object provides reference to the property with [methodname]#getProperty()#. Note that its [methodname]#getValue()# method returns the value with [classname]#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 __PID__s. Items can be further contained in containers and are identified with __Item Identifiers__ or __IID__s. 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 [methodname]#equals(Object)# and [methodname]#hashCode()# methods so that they can be used in any standard Java [classname]#Collection#. The [classname]#Property# interface can be utilized either by implementing the interface or by using some of the built-in property implementations. Vaadin includes a [classname]#Property# interface implementation for arbitrary function pairs and bean properties, with the [classname]#MethodProperty# class, and for simple object properties, with the [classname]#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 [classname]#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 [methodname]#setPropertyDataSource()#. [[datamodel.properties.viewers]] == Property Viewers and Editors The most important function of the [classname]#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 [classname]#Viewer# interface with [methodname]#setPropertyDataSource()#. [source, java] ---- // 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 [classname]#Editor# interface to bind a component that allows editing a particular property type to a property. [source, java] ---- // 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 [classname]#Property# interface, you can bind any component implementing the [classname]#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 [classname]#Label# to the [classname]#TextField# value: [source, java] ---- 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 <>, the validators are executed before writing the value to the property data source, or by calling the [methodname]#validate()# or [methodname]#commit()# for the field. [[datamodel.properties.objectproperty]] == [classname]#ObjectProperty# Implementation The [classname]#ObjectProperty# class is a simple implementation of the [classname]#Property# interface that allows storing an arbitrary Java object. [source, java] ---- // 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); ---- [[datamodel.properties.converter]] == Converting Between Property Type and Representation Fields allow editing a certain type, such as a [classname]#String# or [classname]#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 [interfacename]#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. [[datamodel.properties.converter.basic]] === Basic Use of Converters The [methodname]#setConverter([interfacename]#Converter#)# method sets the converter for a field. The method is defined in [classname]#AbstractField#. [source, java] ---- // Have an integer property final ObjectProperty property = new ObjectProperty(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: [[datamodel.properties.converter.basic.built-in]] .Built-in Converters [options="header"] |=============== |Converter|Representation|Model |[classname]#StringToIntegerConverter#|[classname]#String#|[classname]#Integer# |[classname]#StringToDoubleConverter#|[classname]#String#|[classname]#Double# |[classname]#StringToNumberConverter#|[classname]#String#|[classname]#Number# |[classname]#StringToBooleanConverter#|[classname]#String#|[classname]#Boolean# |[classname]#StringToDateConverter#|[classname]#String#|[classname]#Date# |[classname]#DateToLongConverter#|[classname]#Date#|[classname]#Long# |=============== In addition, there is a [classname]#ReverseConverter# that takes a converter as a parameter and reverses the conversion direction. If a converter already exists for a type, the [methodname]#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. [[datamodel.properties.converter.custom]] === 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 [interfacename]#Converter# interface defined in the [package]#com.vaadin.data.util.converter# package. For example, let us assume that we have a simple [classname]#Complex# type for storing complex values. [source, java] ---- public class ComplexConverter implements Converter { @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 getModelType() { return Complex.class; } @Override public Class getPresentationType() { return String.class; } } ---- The conversion methods get the locale for the conversion as a parameter. [[datamodel.properties.converter.converterfactory]] === 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 [interfacename]#ConverterFactory# interface, it is usually easier to just extend [classname]#DefaultConverterFactory#. [source, java] ---- class MyConverterFactory extends DefaultConverterFactory { @Override public Converter createConverter(Class presentationType, Class modelType) { // Handle one particular type conversion if (String.class == presentationType && Complex.class == modelType) return (Converter) new ComplexConverter(); // Default to the supertype return super.createConverter(presentationType, modelType); } } // Use the factory globally in the application Application.getCurrentApplication().setConverterFactory( new MyConverterFactory()); ---- ifdef::web[] [[datamodel.properties.implementing]] == Implementing the [classname]#Property# Interface Implementation of the [classname]#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 [classname]#Property# interface: [source, java] ---- 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 [methodname]#toString()# method, so it is necessary to override it. To allow editing the value, value returned in the [methodname]#toString()# must be in a format that is accepted by the [methodname]#setValue()# method, unless the property is read-only. The [methodname]#toString()# can perform any type conversion necessary to make the internal type a string, and the [methodname]#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 [classname]#Property.ValueChangeNotifier# and [classname]#Property.ReadOnlyStatusChangeNotifier#. See the [classname]#ObjectProperty# class for an example of the implementation. endif::web[]