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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
|
---
title: Collecting Items in Containers
order: 5
layout: page
---
[[datamodel.container]]
= Collecting Items in Containers
((("[classname]#Container#", id="term.datamodel.container", range="startofrange")))
The [classname]#Container# interface is the highest containment level of the
Vaadin data model, for containing items (rows) which in turn contain properties
(columns). Containers can therefore represent tabular data, which can be viewed
in a [classname]#Table# or some other selection component, as well as
hierarchical data.
The items contained in a container are identified by an __item identifier__ or
__IID__, and the properties by a __property identifier__ or __PID__.
[[datamodel.container.intro]]
== Basic Use of Containers
The basic use of containers involves creating one, adding items to it, and
binding it as a container data source of a component.
[[datamodel.container.intro.default]]
=== Default Containers and Delegation
Before saying anything about creation of containers, it should be noted that all
components that can be bound to a container data source are by default bound to
a default container. For example, [classname]#Table# is bound to a
[classname]#IndexedContainer#, [classname]#Tree# to a
[classname]#HierarchicalContainer#, and so forth.
All of the user interface components using containers also implement the
relevant container interfaces themselves, so that the access to the underlying
data source is delegated through the component.
----
// Create a table with one column
Table table = new Table("My Table");
table.addContainerProperty("col1", String.class, null);
// Access items and properties through the component
table.addItem("row1"); // Create item by explicit ID
Item item1 = table.getItem("row1");
Property property1 = item1.getItemProperty("col1");
property1.setValue("some given value");
// Equivalent access through the container
Container container = table.getContainerDataSource();
container.addItem("row2");
Item item2 = container.getItem("row2");
Property property2 = item2.getItemProperty("col1");
property2.setValue("another given value");
----
[[datamodel.container.intro.creating]]
=== Creating and Binding a Container
A container is created and bound to a component as follows:
----
// Create a container of some type
Container container = new IndexedContainer();
// Initialize the container as required by the container type
container.addContainerProperty("name", String.class, "none");
container.addContainerProperty("volume", Double.class, 0.0);
... add items ...
// Bind it to a component
Table table = new Table("My Table");
table.setContainerDataSource(container);
----
Most components that can be bound to a container allow passing it also in the
constructor, in addition to using [methodname]#setContainerDataSource()#.
Creation of the container depends on its type. For some containers, such as the
[classname]#IndexedContainer#, you need to define the contained properties
(columns) as was done above, while some others determine them otherwise. The
definition of a property with [methodname]#addContainerProperty()# requires a
unique property ID, type, and a default value. You can also give
[parameter]#null#.
Vaadin has a several built-in in-memory container implementations, such as
[classname]#IndexedContainer# and [classname]#BeanItemContainer#, which are easy
to use for setting up nonpersistent data storages. For persistent data, either
the built-in [classname]#SQLContainer# or the [classname]#JPAContainer# add-on
container can be used.
[[datamodel.container.intro.adding]]
=== Adding Items and Accessing Properties
Items can be added to a container with the [methodname]#addItem()# method. The
parameterless version of the method automatically generates the item ID.
----
// Create an item
Object itemId = container.addItem();
----
Properties can be requested from container by first requesting an item with
[methodname]#getItem()# and then getting the properties from the item with
[methodname]#getItemProperty()#.
----
// Get the item object
Item item = container.getItem(itemId);
// Access a property in the item
Property<String> nameProperty =
item.getItemProperty("name");
// Do something with the property
nameProperty.setValue("box");
----
You can also get a property directly by the item and property ids with
[methodname]#getContainerProperty()#.
----
container.getContainerProperty(itemId, "volume").setValue(5.0);
----
[[datamodel.container.intro.givenid]]
=== Adding Items by Given ID
Some containers, such as [classname]#IndexedContainer# and
[classname]#HierarchicalContainer#, allow adding items by a given ID, which can
be any [classname]#Object#.
----
Item item = container.addItem("agivenid");
item.getItemProperty("name").setValue("barrel");
Item.getItemProperty("volume").setValue(119.2);
----
Notice that the actual item __is not__ given as a parameter to the method, only
its ID, as the interface assumes that the container itself creates all the items
it contains. Some container implementations can provide methods to add
externally created items, and they can even assume that the item ID object is
also the item itself. Lazy containers might not create the item immediately, but
lazily when it is accessed by its ID.
[[datamodel.container.inner]]
== Container Subinterfaces
The [classname]#Container# interface contains inner interfaces that container
implementations can implement to fulfill different features required by
components that present container data.
[interfacename]#Container.Filterable#:: Filterable containers allow filtering the contained items by filters, as
described in <<datamodel.container.filtered>>.
[interfacename]#Container.Hierarchical#:: Hierarchical containers allow representing hierarchical relationships between
items and are required by the [classname]#Tree# and [classname]#TreeTable#
components. The [classname]#HierarchicalContainer# is a built-in in-memory
container for hierarchical data, and is used as the default container for the
tree components. The [classname]#FilesystemContainer# provides access to
browsing the content of a file system. Also [classname]#JPAContainer# is
hierarchical, as described in
<<dummy/../../../framework/jpacontainer/jpacontainer-usage#jpacontainer.usage.hierarchical,"Hierarchical
Container">>.
[interfacename]#Container.Indexed#:: An indexed container allows accessing items by an index number, not just their
item ID. This feature is required by some components, especially
[classname]#Table#, which needs to provide lazy access to large containers. The
[classname]#IndexedContainer# is a basic in-memory implementation, as described
in <<datamodel.container.indexedcontainer>>.
[interfacename]#Container.Ordered#:: An ordered container allows traversing the items in successive order in either
direction. Most built-in containers are ordered.
[interfacename]#Container.SimpleFilterable#:: This interface enables filtering a container by string matching with
[methodname]#addContainerFilter()#. The filtering is done by either searching
the given string anywhere in a property value, or as its prefix.
[interfacename]#Container.Sortable#:: A sortable container is required by some components that allow sorting the
content, such as [classname]#Table#, where the user can click a column header to
sort the table by the column. Some other components, such as
[classname]#Calendar#, may require that the content is sorted to be able to
display it properly. Depending on the implementation, sorting can be done only
when the [methodname]#sort()# method is called, or the container is
automatically kept in order according to the last call of the method.
See the API documentation for a detailed description of the interfaces.
[[datamodel.container.indexedcontainer]]
== [classname]#IndexedContainer#
The [classname]#IndexedContainer# is an in-memory container that implements the
[interfacename]#Indexed# interface to allow referencing the items by an index.
[classname]#IndexedContainer# is used as the default container in most selection
components in Vaadin.
The properties need to be defined with [methodname]#addContainerProperty()#,
which takes the property ID, type, and a default value. This must be done before
any items are added to the container.
----
// Create the container
IndexedContainer container = new IndexedContainer();
// Define the properties (columns)
container.addContainerProperty("name", String.class, "noname");
container.addContainerProperty("volume", Double.class, -1.0d);
// Add some items
Object content[][] = { {"jar", 2.0}, {"bottle", 0.75},
{"can", 1.5}};
for (Object[] row: content) {
Item newItem = container.getItem(container.addItem());
newItem.getItemProperty("name").setValue(row[0]);
newItem.getItemProperty("volume").setValue(row[1]);
}
----
New items are added with [methodname]#addItem()#, which returns the item ID of
the new item, or by giving the item ID as a parameter as was described earlier.
Note that the [classname]#Table# component, which has
[classname]#IndexedContainer# as its default container, has a conveniency
[methodname]#addItem()# method that allows adding items as object vectors
containing the property values.
The container implements the [interfacename]#Container.Indexed# feature to allow
accessing the item IDs by their index number, with [methodname]#getIdByIndex()#,
etc. The feature is required mainly for internal purposes of some components,
such as [classname]#Table#, which uses it to enable lazy transmission of table
data to the client-side.
[[datamodel.container.beancontainer]]
== [classname]#BeanContainer#
The [classname]#BeanContainer# is an in-memory container for JavaBean objects.
Each contained bean is wrapped inside a [classname]#BeanItem# wrapper. The item
properties are determined automatically by inspecting the getter and setter
methods of the class. This requires that the bean class has public visibility,
local classes for example are not allowed. Only beans of the same type can be
added to the container.
The generic has two parameters: a bean type and an item identifier type. The
item identifiers can be obtained by defining a custom resolver, using a specific
item property for the IDs, or by giving item IDs explicitly. As such, it is more
general than the [classname]#BeanItemContainer#, which uses the bean object
itself as the item identifier, making the use usually simpler. Managing the item
IDs makes [classname]#BeanContainer# more complex to use, but it is necessary in
some cases where the [methodname]#equals()# or [methodname]#hashCode()# methods
have been reimplemented in the bean.
----
// Here is a JavaBean
public class Bean implements Serializable {
String name;
double energy; // Energy content in kJ/100g
public Bean(String name, double energy) {
this.name = name;
this.energy = energy;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getEnergy() {
return energy;
}
public void setEnergy(double energy) {
this.energy = energy;
}
}
void basic(VerticalLayout layout) {
// Create a container for such beans with
// strings as item IDs.
BeanContainer<String, Bean> beans =
new BeanContainer<String, Bean>(Bean.class);
// Use the name property as the item ID of the bean
beans.setBeanIdProperty("name");
// Add some beans to it
beans.addBean(new Bean("Mung bean", 1452.0));
beans.addBean(new Bean("Chickpea", 686.0));
beans.addBean(new Bean("Lentil", 1477.0));
beans.addBean(new Bean("Common bean", 129.0));
beans.addBean(new Bean("Soybean", 1866.0));
// Bind a table to it
Table table = new Table("Beans of All Sorts", beans);
layout.addComponent(table);
}
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beancontainer.basic[on-line example, window="_blank"].
To use explicit item IDs, use the methods [methodname]#addItem(Object, Object)#,
[methodname]#addItemAfter(Object, Object, Object)#, and
[methodname]#addItemAt(int, Object, Object)#.
It is not possible to add additional properties to the container, except
properties in a nested bean.
[[datamodel.container.beancontainer.nestedproperties]]
=== Nested Properties
((("nested bean properties", id="term.datamodel.container.beancontainer.nestedproperties", range="startofrange")))
If you have a nested bean with an 1:1 relationship inside a bean type contained
in a [classname]#BeanContainer# or [classname]#BeanItemContainer#, you can add
its properties to the container by specifying them with
[methodname]#addNestedContainerProperty()#. The feature is defined at the level
of [classname]#AbstractBeanContainer#.
((("[methodname]#addNestedContainerProperty()#")))
As with the bean in a bean container, also a nested bean must have public
visibility or otherwise an access exception is thrown. An intermediate reference
from a bean in the bean container to a nested bean may have a null value.
For example, let us assume that we have the following two beans with the first
one nested inside the second one.
----
/** Bean to be nested */
public class EqCoord implements Serializable {
double rightAscension; /* In angle hours */
double declination; /* In degrees */
... setters and getters for the properties ...
}
/** Bean referencing a nested bean */
public class Star implements Serializable {
String name;
EqCoord equatorial; /* Nested bean */
... setters and getters for the properties ...
}
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.nestedbean[on-line example, window="_blank"].
After creating the container, you can declare the nested properties by
specifying their property identifiers with the
[methodname]#addNestedContainerProperty()# in dot notation.
----
// Create a container for beans
BeanItemContainer<Star> stars =
new BeanItemContainer<Star>(Star.class);
// Declare the nested properties to be used in the container
stars.addNestedContainerProperty("equatorial.rightAscension");
stars.addNestedContainerProperty("equatorial.declination");
// Add some items
stars.addBean(new Star("Sirius", new EqCoord(6.75, 16.71611)));
stars.addBean(new Star("Polaris", new EqCoord(2.52, 89.26417)));
// Here the nested bean reference is null
stars.addBean(new Star("Vega", null));
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.nestedbean[on-line example, window="_blank"].
If you bind such a container to a [classname]#Table#, you probably also need to
set the column headers. Notice that the entire nested bean itself is still a
property in the container and would be displayed in its own column. The
[methodname]#toString()# method is used for obtaining the displayed value, which
is by default an object reference. You normally do not want this, so you can
hide the column with [methodname]#setVisibleColumns()#.
((("[methodname]#setVisibleColumns()#")))
----
// Put them in a table
Table table = new Table("Stars", stars);
table.setColumnHeader("equatorial.rightAscension", "RA");
table.setColumnHeader("equatorial.declination", "Decl");
table.setPageLength(table.size());
// Have to set explicitly to hide the "equatorial" property
table.setVisibleColumns("name",
"equatorial.rightAscension", "equatorial.declination");
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.nestedbean[on-line example, window="_blank"].
The resulting table is shown in
<<figure.datamodel.container.beancontainer.nestedproperties>>.
[[figure.datamodel.container.beancontainer.nestedproperties]]
.[classname]#Table# Bound to a [classname]#BeanContainer# with Nested Properties
image::img/beanitemcontainer-nested-beans.png[]
The bean binding in [classname]#AbstractBeanContainer# normally uses the
[classname]#MethodProperty# implementation of the [classname]#Property#
interface to access the bean properties using the setter and getter methods. For
nested properties, the [classname]#NestedMethodProperty# implementation is used.
((("[classname]#MethodProperty#")))
((("[classname]#NestedMethodProperty#")))
(((range="endofrange", startref="term.datamodel.container.beancontainer.nestedproperties")))
ifdef::web[]
[[datamodel.container.beancontainer.idresolver]]
=== Defining a Bean ID Resolver
If a bean ID resolver is set using [methodname]#setBeanIdResolver()# or
[methodname]#setBeanIdProperty()#, the methods [methodname]#addBean()#,
[methodname]#addBeanAfter()#, [methodname]#addBeanAt()# and
[methodname]#addAll()# can be used to add items to the container. If one of
these methods is called, the resolver is used to generate an identifier for the
item (must not return [parameter]#null#).
Note that explicit item identifiers can also be used when a resolver has been
set by calling the [methodname]#addItem*()# methods - the resolver is only used
when adding beans using the [methodname]#addBean*()# or
[methodname]#addAll(Collection)# methods.
endif::web[]
[[datamodel.container.beanitemcontainer]]
== [classname]#BeanItemContainer#
[classname]#BeanItemContainer# is a container for JavaBean objects where each
bean is wrapped inside a [classname]#BeanItem# wrapper. The item properties are
determined automatically by inspecting the getter and setter methods of the
class. This requires that the bean class has public visibility, local classes
for example are not allowed. Only beans of the same type can be added to the
container.
[classname]#BeanItemContainer# is a specialized version of the
[classname]#BeanContainer# described in <<datamodel.container.beancontainer>>.
It uses the bean itself as the item identifier, which makes it a bit easier to
use than [classname]#BeanContainer# in many cases. The latter is, however,
needed if the bean has reimplemented the [methodname]#equals()# or
[methodname]#hashCode()# methods.
Let us revisit the example given in <<datamodel.container.beancontainer>> using
the [classname]#BeanItemContainer#.
----
// Create a container for the beans
BeanItemContainer<Bean> beans =
new BeanItemContainer<Bean>(Bean.class);
// Add some beans to it
beans.addBean(new Bean("Mung bean", 1452.0));
beans.addBean(new Bean("Chickpea", 686.0));
beans.addBean(new Bean("Lentil", 1477.0));
beans.addBean(new Bean("Common bean", 129.0));
beans.addBean(new Bean("Soybean", 1866.0));
// Bind a table to it
Table table = new Table("Beans of All Sorts", beans);
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.basic[on-line example, window="_blank"].
It is not possible to add additional properties to a
[classname]#BeanItemContainer#, except properties in a nested bean, as described
in <<datamodel.container.beancontainer>>. ((("nested bean
properties")))
ifdef::web[]
[[datamodel.container.iterating]]
== Iterating Over a Container
As the items in a [classname]#Container# are not necessarily indexed, iterating
over the items has to be done using an [classname]#Iterator#. The
[methodname]#getItemIds()# method of [classname]#Container# returns a
[classname]#Collection# of item identifiers over which you can iterate. The
following example demonstrates a typical case where you iterate over the values
of check boxes in a column of a [classname]#Table# component. The context of the
example is the example used in
<<dummy/../../../framework/components/components-table#components.table,"Table">>.
----
// Collect the results of the iteration into this string.
String items = "";
// Iterate over the item identifiers of the table.
for (Iterator i = table.getItemIds().iterator(); i.hasNext();) {
// Get the current item identifier, which is an integer.
int iid = (Integer) i.next();
// Now get the actual item from the table.
Item item = table.getItem(iid);
// And now we can get to the actual checkbox object.
Button button = (Button)
(item.getItemProperty("ismember").getValue());
// If the checkbox is selected.
if ((Boolean)button.getValue() == true) {
// Do something with the selected item; collect the
// first names in a string.
items += item.getItemProperty("First Name")
.getValue() + " ";
}
}
// Do something with the results; display the selected items.
layout.addComponent (new Label("Selected items: " + items));
----
Notice that the [methodname]#getItemIds()# returns an __unmodifiable
collection__, so the [classname]#Container# may not be modified during
iteration. You can not, for example, remove items from the
[classname]#Container# during iteration. The modification includes modification
in another thread. If the [classname]#Container# is modified during iteration, a
[classname]#ConcurrentModificationException# is thrown and the iterator may be
left in an undefined state.
endif::web[]
[[datamodel.container.gpc]]
== [classname]#GeneratedPropertyContainer#
[classname]#GeneratedPropertyContainer# is a container wrapper that allows
defining generated values for properties (columns). The generated properties can
shadow properties with the same IDs in the wrapped container. Removing a
property from the wrapper hides it.
The container is especially useful with [classname]#Grid#, which does not
support generated columns or hiding columns like [classname]#Table# does.
[[datamodel.container.gpc.wrapping]]
=== Wrapping a Container
A container to be wrapped must be a [interfacename]#Container.Indexed#. It can
optionally also implement [interfacename]#Container.Sortable# or
[interfacename]#Container.Filterable# to enable sorting and filtering the
container, respectively.
For example, let us consider the following container with some regular columns:
----
IndexedContainer container = new IndexedContainer();
container.addContainerProperty("firstname", String.class, null);
container.addContainerProperty("lastname", String.class, null);
container.addContainerProperty("born", Integer.class, null);
container.addContainerProperty("died", Integer.class, null);
// Wrap it
GeneratedPropertyContainer gpcontainer =
new GeneratedPropertyContainer(container);
----
[[datamodel.container.gpc.properties]]
=== Generated Properties
Now, you can add generated properties in the container with
[methodname]#addGeneratedProperty()# by specifying a property ID and a
[interfacename]#PropertyValueGenerator#. The method takes the ID of the
generated property as first parameter; you can use a same ID as in the wrapped
container to shadow its properties.
You need to implement [methodname]#getType()#, which must return the class
object of the value type of the property, and [methodname]#getValue()#, which
returns the property value for the given item. The item ID and the property ID
of the generated property are also given in case they are needed. You can access
other properties of the item to compute the property value.
----
gpcontainer.addGeneratedProperty("lived",
new PropertyValueGenerator<Integer>() {
@Override
public Integer getValue(Item item, Object itemId,
Object propertyId) {
int born = (Integer)
item.getItemProperty("born").getValue();
int died = (Integer)
item.getItemProperty("died").getValue();
return Integer.valueOf(died - born);
}
@Override
public Class<Integer> getType() {
return Integer.class;
}
});
----
You can access other items in the container, also their generated properties,
although you should beware of accidental recursion.
[[datamodel.container.gpc.using]]
=== Using [classname]#GeneratedPropertyContainer#
Finally, you need to bind the [classname]#GeneratedPropertyContainer# to the
component instead of the wrapped container.
----
Grid grid = new Grid(gpcontainer);
----
When using [classname]#GeneratedPropertyContainer# in [classname]#Grid#, notice
that generated columns are read-only, so you can not add grid rows with
[methodname]#addRow()#. In editable mode, editor fields are not generated for
generated columns.
[[datamodel.container.gpc.sorting]]
=== Sorting
Even though the [classname]#GeneratedPropertyContainer# implements
[interfacename]#Container.Sortable#, the wrapped container must also support it
or otherwise sorting is disabled. Also, the generated properties are not
normally sortable, but require special handling to enable sorting.
[[datamodel.container.filtered]]
== [classname]#Filterable# Containers
((("Container", "Filterable", id="term.datamodel.container.filtered.filterable", range="startofrange")))
((("[classname]#Filter# (in [classname]#Container#)", id="term.datamodel.container.filtered.filters", range="startofrange")))
Containers that implement the [classname]#Container.Filterable# interface can be
filtered. For example, the built-in [classname]#IndexedContainer# and the bean
item containers implement it. Filtering is typically used for filtering the
content of a [classname]#Table#.
((("[classname]#IndexedContainer#")))
((("[classname]#Table#")))
Filters implement the [classname]#Filter# interface and you add them to a
filterable container with the [methodname]#addContainerFilter()# method.
Container items that pass the filter condition are kept and shown in the
filterable component.
((("[methodname]#addContainerFilter()#")))
----
Filter filter = new SimpleStringFilter("name",
"Douglas", true, false);
table.addContainerFilter(filter);
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.basic[on-line example, window="_blank"].
If multiple filters are added to a container, they are evaluated using the
logical AND operator so that only items that are passed by all the filters are
kept.
[[datamodel.container.filtered.composite]]
=== Atomic and Composite Filters
Filters can be classified as __atomic__ and __composite__. Atomic filters, such
as [classname]#SimpleStringFilter#, define a single condition, usually for a
specific container property. Composite filters make filtering decisions based on
the result of one or more other filters. The built-in composite filters
implement the logical operators AND, OR, or NOT.
For example, the following composite filter would filter out items where the
[literal]#++name++# property contains the name "Douglas" somewhere __or__ where
the [literal]#++age++# property has value less than 42. The properties must have
[classname]#String# and [classname]#Integer# types, respectively.
----
filter = new Or(new SimpleStringFilter("name",
"Douglas", true, false),
new Compare.Less("age", 42));
----
[[datamodel.container.filtered.builtin]]
=== Built-In Filter Types
The built-in filter types are the following:
[classname]#SimpleStringFilter#:: ((("[classname]#SimpleStringFilter#")))
+
Passes items where the specified property, that must be of [classname]#String#
type, contains the given [parameter]#filterString# as a substring. If
[parameter]#ignoreCase# is [parameter]#true#, the search is case insensitive. If
the [parameter]#onlyMatchPrefix# is [parameter]#true#, the substring may only be
in the beginning of the string, otherwise it may be elsewhere as well.
[classname]#IsNull#:: ((("[classname]#IsNull# (filter)")))
+
Passes items where the specified property has null value. For in-memory
filtering, a simple [literal]#++==++# check is performed. For other containers,
the comparison implementation is container dependent, but should correspond to
the in-memory null check.
[classname]#Equal#, [classname]#Greater#, [classname]#Less#, [classname]#GreaterOrEqual#, and [classname]#LessOrEqual#:: ((("[classname]#Equal# (filter)")))
((("[classname]#Greater# (filter)")))
((("[classname]#Less# (filter)")))
((("[classname]#GreaterOrEqual# (filter)")))
((("[classname]#LessOrEqual# (filter)")))
The comparison filter implementations compare the specified property value to
the given constant and pass items for which the comparison result is true. The
comparison operators are included in the abstract [classname]#Compare# class.
+
For the [classname]#Equal# filter, the [methodname]#equals()# method for the
property is used in built-in in-memory containers. In other types of containers,
the comparison is container dependent and may use, for example, database
comparison operations.
+
For the other filters, the property value type must implement the
[classname]#Comparable# interface to work with the built-in in-memory
containers. Again for the other types of containers, the comparison is container
dependent.
[classname]#And# and [classname]#Or#:: ((("[classname]#And# (filter)")))
((("[classname]#Or# (filter)")))
+
These logical operator filters are composite filters that combine multiple other
filters.
[classname]#Not#:: ((("[classname]#Not# (filter)")))
+
The logical unary operator filter negates which items are passed by the filter
given as the parameter.
[[datamodel.container.filtered.custom]]
=== Implementing Custom Filters
A custom filter needs to implement the [classname]#Container.Filter# interface.
A filter can use a single or multiple properties for the filtering logic. The
properties used by the filter must be returned with the
[methodname]#appliesToProperty()# method. If the filter applies to a
user-defined property or properties, it is customary to give the properties as
the first argument for the constructor of the filter.
----
class MyCustomFilter implements Container.Filter {
protected String propertyId;
protected String regex;
public MyCustomFilter(String propertyId, String regex) {
this.propertyId = propertyId;
this.regex = regex;
}
/** Tells if this filter works on the given property. */
@Override
public boolean appliesToProperty(Object propertyId) {
return propertyId != null &&
propertyId.equals(this.propertyId);
}
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.custom[on-line example, window="_blank"].
The actual filtering logic is done in the [methodname]#passesFilter()# method,
which simply returns [literal]#++true++# if the item should pass the filter and
[literal]#++false++# if it should be filtered out.
----
/** Apply the filter on an item to check if it passes. */
@Override
public boolean passesFilter(Object itemId, Item item)
throws UnsupportedOperationException {
// Acquire the relevant property from the item object
Property p = item.getItemProperty(propertyId);
// Should always check validity
if (p == null || !p.getType().equals(String.class))
return false;
String value = (String) p.getValue();
// The actual filter logic
return value.matches(regex);
}
}
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.custom[on-line example, window="_blank"].
You can use such a custom filter just like any other:
----
c.addContainerFilter(
new MyCustomFilter("Name", (String) tf.getValue()));
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.custom[on-line example, window="_blank"].
(((range="endofrange", startref="term.datamodel.container.filtered.filters")))
(((range="endofrange", startref="term.datamodel.container.filtered.filterable")))
(((range="endofrange", startref="term.datamodel.container")))
|