123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- ---
- 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
- <<dummy/../../../framework/components/components-fields#components.fields.validation,"Field
- Validation">>, 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<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:
-
- [[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<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.
-
-
- [[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 <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());
- ----
-
-
-
- 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[]
|