summaryrefslogtreecommitdiffstats
path: root/documentation/datamodel/datamodel-items.asciidoc
blob: 75a7c1ecf81b0456a66062358e0714f900ec4f06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
---
title: Holding properties in Items
order: 3
layout: page
---

[[datamodel.items]]
= Holding properties in Items

The [classname]#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 [classname]#Item# with
[methodname]#getItemProperty()# using the identifier.

Examples on the use of items include rows in a [classname]#Table#, with the
properties corresponding to table columns, nodes in a [classname]#Tree#, and the
the data bound to a [classname]#Form#, with item's properties bound to
individual form fields.

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 [classname]#Item# interface is to use
existing implementations. Provided utility classes include a configurable
property set ( [classname]#PropertysetItem#) and a bean-to-item adapter (
[classname]#BeanItem#). Also, a [classname]#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 [classname]#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 [classname]#Item# interface defines inner interfaces for maintaining the
item property set and listening changes made to it.
[classname]#PropertySetChangeEvent# events can be emitted by a class
implementing the [classname]#PropertySetChangeNotifier# interface. They can be
received through the [classname]#PropertySetChangeListener# interface.

ifdef::web[]
[[datamodel.items.propertysetitem]]
== The [classname]#PropertysetItem# Implementation

The [classname]#PropertysetItem# is a generic implementation of the
[classname]#Item# interface that allows storing properties. The properties are
added with [methodname]#addItemProperty()#, which takes a name and the property
as parameters.

The following example demonstrates a typical case of collecting
[classname]#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);
----

endif::web[]

[[datamodel.items.beanitem]]
== Wrapping a Bean in a [classname]#BeanItem#

The [classname]#BeanItem# implementation of the [classname]#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
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 [methodname]#getBean()# method to get a reference to the
underlying bean.

[[datamodel.items.beanitem.nested]]
=== Nested Beans

You may often have composite classes where one class "has a" another class. For
example, consider the following [classname]#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 [classname]#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
[classname]#MethodProperty# or [classname]#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 [classname]#NestedMethodProperty# does not access the
nested bean immediately but only when accessing the property values, while when
using [classname]#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 [classname]#Form# as follows:


----
// Bind it to a component
Form form = new Form();
form.setItemDataSource(item);
    
// Nicer captions
form.getField("discoverername").setCaption("Discoverer");
form.getField("discovererborn").setCaption("Born");
----

[[figure.datamodel.items.beanitem.nested]]
.A [classname]#Form# with Nested Bean Properties
image::img/beanitem-nested-beans.png[]

The [classname]#BeanContainer# and [classname]#BeanItemContainer# allow easy
definition of nested bean properties with
[methodname]#addNestedContainerProperty()#, as described in
<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.beancontainer.nestedproperties,"Nested
Properties">>.