Copyright © 2016-2018
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
1. Introduction
The Holon Vaadin 7 module represents the platform support for the Vaadin 7.7.x web applications framework.
This module makes available an API which matches at 99% the official Holon Vaadin module API (which requires Vaadin version 8.1.x), only not compatible classes or methods are different for obvious reasons.
The main differences between the two modules API are:
-
The Vaadin 7 module supports the
com.vaadin.ui.Field
interface forInput
components andProperty
renderers, while the Vaadin 8 module relies on theHasValue
interface -
The Vaadin 7
Table
component is available asItemListing
backing component, in addition to theGrid
component -
The
com.vaadin.data.provider.DataProvider
interface is not available in Vaadin 7, so it is not supported as item components data source
See the official Holon Vaadin module documentation to use Vaadin version 8 instead.
1.1. Sources and contributions
The Holon Platform Vaadin 7 module source code is available from the GitHub repository https://github.com/holon-platform/holon-vaadin7.
See the repository README
file for information about:
-
The source code structure.
-
How to build the module artifacts from sources.
-
Where to find the code examples.
-
How to contribute to the module development.
2. Obtaining the artifacts
The Holon Platform uses Maven for projects build and configuration. All the platform artifacts are published in the Maven Central Repository, so there is no need to explicitly declare additional repositories in your project pom
file.
At the top of each section of this documentation you will find the Maven coordinates (group id, artifact id and version) to obtain the artifact(s) as a dependency for your project.
A BOM (Bill Of Materials) pom
is provided to import the available dependencies for a specific version in your projects. The Maven coordinates for the core BOM are the following:
Maven coordinates:
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-vaadin-bom</artifactId>
<version>5.2.4</version>
The BOM can be imported in a Maven project in the following way:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-vaadin-bom</artifactId>
<version>5.2.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.1. Using the Platform BOM
The Holon Platform provides an overall Maven BOM (Bill of Materials) to easily obtain all the available platform artifacts.
See Obtain the platform artifacts for details.
3. What’s new in version 5.2.x
-
Support for Spring version 5+ and Spring Boot 2.1+
-
Support for JDK 9+ module system using
Automatic-Module-Name
.
4. Vaadin integration
The holon-vaadin
artifact is the main entry point to use the Holon platform Vaadin integration.
Maven coordinates:
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-vaadin</artifactId>
<version>5.2.4</version>
5. Component builders
A complete set of fluent builders is available to create and configure the most common Vaadin standard components and the ones provided by the Holon platform Vaadin integration module.
See UI Components for a list of the additional components made available by the Holon platform Vaadin integration module. |
All the builders can be obtained using the Components interface, which is organized in sub-interfaces by component kind.
Sub-interface | Provides |
---|---|
[NONE] |
A set of configure(*) methods to setup existing standard component instances and a set of methods to obtain the fluent builders for standard Vaadin components, such as |
input |
Builders for different data type |
view |
Builders for |
listing |
Builders for components which represent a list of items. |
For each UI displayed text of a component (for example, the component caption or description), the builders provide the opportunity to set a localizable text, using a Localizable object or directly providing the message code and the default message of the localizable text. In order for the localization to work, a LocalizationContext must be available as a context resource. See the Internationalization documentation for further information.
|
6. UI Components
The Vaadin module provides a set of components which extend or decorates the standard components set. The available components are described below.
6.1. Input
The Input interface represents a component that has a user-editable value, which can be read and setted using the methods provided by the The ValueHolder super interface.
Input components supports the read-only (to prevent the user from changing the value) and required (to show a suitable indicator along with the field) modes.
Input components supports ValueChangeListener
registration to listen to input value changes.
Input<String> stringInput = Components.input.string().caption("String input").inputPrompt("Value here")
.maxLength(100).styleName("mystyle").build(); (1)
stringInput.setValue("test"); (2)
String value = stringInput.getValue(); (3)
stringInput.addValueChangeListener(e -> System.out.println("Value: " + e.getValue())); (4)
1 | Create and configure a String value type Input field. |
2 | Set a value |
3 | Get the value |
4 | Add a ValueChangeListener |
The concrete Vaadin Component
, which represents the UI input element, can be obtained through the getComponent()
method.
Input<LocalDate> dateInput = Components.input.localDate().caption("Date input").lenient(true).build(); (1)
Component inputComponent = dateInput.getComponent(); (2)
1 | Create and configure a LocalDate value type Input field. |
2 | Get the input Component , which can be used to display the input field in UI. |
An Input
component can be obtained from a standard Vaadin Field
component using the from(…)
static method.
Input<String> stringInput = Input.from(new TextField());
Another way to obtain a standard Vaadin Field
component from an input component is to use the asField()
input builder method.
Field<Locale> localeField = Components.input.singleSelect(Locale.class).items(Locale.US, Locale.CANADA)
.caption("Select Locale").fullWidth().withValue(Locale.US).asField(); (1)
1 | Create select field using Locale data type and obtain it as Field component. |
6.2. ValidatableInput
The ValidatableInput interface represents an Input
component which supports value validation using the standard Holon platform Validator
interface.
Value validation can be triggered each time the input value changes, using setValidateOnValueChange
method.
A ValidationStatusHandler can be used to control how the validation errors are notified to the user, listening to validation status change events.
By default, the standard Vaadin component error notification is used.
Input<String> stringInput = Components.input.string().build();
ValidatableInput<String> validatableInput = ValidatableInput.from(stringInput); (1)
validatableInput.addValidator(Validator.email()); (2)
validatableInput.addValidator(Validator.max(100)); (3)
validatableInput.setValidationStatusHandler(e -> { (4)
if (e.isInvalid()) {
Notification.show(e.getErrorMessage(), Type.ERROR_MESSAGE);
}
});
validatableInput.validate(); (5)
validatableInput.setValidateOnValueChange(true); (6)
1 | Create a ValidatableInput from a String input |
2 | Add a validator to check the input value is a valid e-mail address |
3 | Add a validator to check the input value is maximum 100 characters long |
4 | Set validation status handler which shows a notification when the input in invalid |
5 | Perform the input value validation |
6 | Triggers validation when input value changes |
A ValidatableInput
can be obtained from an existing Input
or Field
component instance or using the appropriate builder.
ValidatableInput<String> validatableInput = ValidatableInput.from(Components.input.string().build()); (1)
validatableInput = ValidatableInput.from(new TextField()); (2)
validatableInput = Components.input.string().validatable() (3)
.required("Value is required") (4)
.withValidator(Validator.max(100)).build(); (5)
1 | Create a ValidatableInput from a String Input |
2 | Create a ValidatableInput from a String type Field component |
3 | Create a ValidatableInput using the input builder |
4 | Add a default required (the input is not empty) validator using the provided error message |
5 | Add a standard validator |
6.3. Selectable Input
When the Input values must be selected from a specific set, a selectable Input type can be used. Two selectable Input types are available:
-
The SingleSelect Input, in which at most one item can be selected at a time;
-
The MultiSelect Input, in which multiple items can be selected at the same time.
Both selectable Input types extends the Selectable interface, which provides methods to check if some item is selected, obtain the selected item/s and change the current selection. Furthermore, a SelectionListener
can be registered to listen to selection changes.
SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class).caption("Single select")
.build(); (1)
singleSelect.setValue(new TestData(1)); (2)
singleSelect.select(new TestData(1)); (3)
singleSelect.clear(); (4)
singleSelect.deselectAll(); (5)
boolean selected = singleSelect.isSelected(new TestData(1)); (6)
singleSelect.addSelectionListener(
s -> s.getFirstSelectedItem().ifPresent(i -> Notification.show("Selected: " + i.getId()))); (7)
1 | Create a SingleSelect Input using the TestData class as selection item type |
2 | Select a value using the default setValue() Input method |
3 | Select a value using select() method |
4 | Deselect the value using the default clear() Input method |
5 | Deselect the value using the deselectAll() method |
6 | Check whether a value is selected |
7 | Add a selection listener |
MultiSelect<TestData> multiSelect = Components.input.multiSelect(TestData.class).caption("Multi select")
.build(); (1)
Set<TestData> values = new HashSet<>();
values.add(new TestData(1));
values.add(new TestData(2));
multiSelect.setValue(values); (2)
multiSelect.select(new TestData(3)); (3)
multiSelect.deselect(new TestData(3)); (4)
multiSelect.clear(); (5)
multiSelect.deselectAll(); (6)
boolean selected = multiSelect.isSelected(new TestData(1)); (7)
multiSelect.addSelectionListener(s -> Notification.show(s.getAllSelectedItems().stream()
.map(i -> String.valueOf(i.getId())).collect(Collectors.joining(";", "Selected: ", "")))); (8)
1 | Create a MultiSelect Input using the TestData class as selection item type |
2 | Select a set of values using the default setValue() Input method |
3 | Add a value to current selection using the select() method |
4 | Remove a value from current selection |
5 | Deselect the value using the default clear() Input method |
6 | Deselect the value using the deselectAll() method |
7 | Check whether a value is selected |
8 | Add a selection listener |
6.3.1. Rendering mode
The visual aspect of the selectable Input can be configured using the RenderingMode
enumeration. According to the rendering mode, a suitable UI component will be used to implement the Input.
The default rendering modes are SELECT for SingleSelect inputs and OPTIONS for MultiSelect inputs.
|
Rendering mode | SingleSelect UI component | MultiSelect UI component |
---|---|---|
NATIVE_SELECT |
|
not supported |
SELECT |
|
|
OPTIONS |
|
|
SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class, RenderingMode.OPTIONS)
.build(); (1)
MultiSelect<TestData> multiSelect = Components.input.multiSelect(TestData.class, RenderingMode.SELECT).build(); (2)
1 | Create a SingleSelect using the OPTIONS rendering mode: a CheckBox group UI component will be used |
2 | Create a MultiSelect using the SELECT rendering mode: a ListSelect UI component will be used |
6.3.2. Selection items data source
The data source of the available items for a selectable input can be configured in the following ways:
-
Using a fixed set of items, which can be provided using the
addItem(…)
oritems(…)
methods of the select input builder; -
Using a Holon platform
ItemDataProvider
data provider;
SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class)
.items(new TestData(1), new TestData(2)).build(); (1)
singleSelect = Components.input.singleSelect(TestData.class)
.dataSource(ItemDataProvider.create(q -> 2, (q, o, l) -> Stream.of(new TestData(1), new TestData(2))))
.build(); (2)
1 | Create a select input using an explicitly provided items set |
2 | Create a select input using a Holon platform ItemDataProvider |
6.3.3. Item captions and icons
The selectable input builders allow to explicitly set the selection item captions and icons. As for the items caption, the Holon platform localization architecture is supported, and the builders provides methods to specify the caption using a Localizable
or a message code.
In order for the localization to work, a LocalizationContext must be available as a context resource. See the Internationalization documentation for further information.
|
final TestData ONE = new TestData(1);
final TestData TWO = new TestData(2);
SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class).items(ONE, TWO)
.itemCaption(ONE, "One") (1)
.itemCaption(ONE, "One", "caption-one-message-code") (2)
.itemIcon(ONE, FontAwesome.STAR) (3)
.build();
1 | Set the item caption for the ONE item |
2 | Set the localizable item caption for the ONE item, providing a default caption and a translation message code |
3 | Set the item icon for the ONE item |
Furthermore, the ItemCaptionGenerator
and the ItemIconGenerator
interfaces can be used to provide custom selection items captions and icons. By default, the item caption is obtained using the toString()
method of the item class and no icon is displayed.
SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class)
.items(new TestData(1), new TestData(2)) // set the items
.itemCaptionGenerator(i -> i.getDescription()) (1)
.itemIconGenerator(i -> i.getId() == 1 ? FontAwesome.STAR : FontAwesome.STAR_O) (2)
.build();
1 | Set an item caption generator which provides the item description as item caption |
2 | Set an item icon generator |
6.3.4. SingleSelect caption filter
For SingleSelect
inputs using SELECT rendering mode, the UI component allows the user to type a filtering text to search for a selection item, which is referred to item captions.
The FilteringMode
can be changed using the filteringMode(…)
builder method.
SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class)
.items(new TestData(1), new TestData(2)) // set the items
.filteringMode(FilteringMode.CONTAINS) (1)
.build();
1 | Set the caption filtering mode |
6.3.5. Using the Property
model with selectable Inputs
The Holon platform Property
model is fully supported by the selectable input builder, which allow to create selectable inputs using PropertyBox
type items and a specific Property
as selection property (typically, the property which acts as item id).
The selection data type will be the same as the selection property type.
The simplest way to configure a Property
based selectable input, is by using a Datastore
.
See the Property documentation for further information about the Holon platform Property model.
|
Datastore datastore = obtainDatastore();
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
SingleSelect<Long> singleSelect = Components.input.singleSelect(ID) (1)
.dataSource(datastore, DataTarget.named("testData"), PropertySet.of(ID, DESCRIPTION)) (2)
.itemCaptionGenerator(propertyBox -> propertyBox.getValue(DESCRIPTION)) (3)
.build();
singleSelect.setValue(Long.valueOf(1)); (4)
Long selectedId = singleSelect.getValue(); (5)
singleSelect.refresh(); (6)
1 | Create a SingleSelect using the ID property as selection property |
2 | Set the select data source using a Datastore , specifying the DataTarget and the set of property to use as query projection |
3 | Set an item caption generator which provides the value of the DESCRIPTION property as item caption |
4 | Set the selected value, which will be of type Long , consistent with the ID selection property |
5 | Get the selected value, which will be of type Long , consistent with the ID selection property |
6 | Refresh the selection items, querying the Datastore |
One or more QueryConfigurationProvider
can be used to control the Datastore query configuration, providing additional filters and/or sorts.
Datastore datastore = obtainDatastore();
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
SingleSelect<Long> singleSelect = Components.input.singleSelect(ID)
.dataSource(datastore, DataTarget.named("testData"), PropertySet.of(ID, DESCRIPTION)) //
.withQueryConfigurationProvider(() -> ID.gt(0L)) (1)
.itemCaptionGenerator(propertyBox -> propertyBox.getValue(DESCRIPTION)).build();
1 | Add a QueryConfigurationProvider which provides a QueryFilter to select only the items with the ID property that is greater than 0 |
6.4. Grouping Inputs
The PropertyInputGroup component allows to group and manage a set of Input
fields, relying on the Property
model and using PropertyBox
objects to collect, set and provide the Input values.
See the Property documentation for further information about the Holon platform Property model.
|
A PropertyInputGroup
is bound to a set of properties, and for each property an Input
field is made available to set and get the property value. Each property has to be bound to an Input
field with a consistent value type.
The property values are setted and obtained using the PropertyBox
data container, with the same property set of the input group.
The property values are automatically updated in the current PropertyBox
according to any user modification made through the input’s UI components.
The property input group supports ValueChangeListener
registration to be notified when the group PropertyBox
value changes.
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) (1)
.bind(ID, Components.input.number(Long.class).build()) (2)
.bind(DESCRIPTION, Components.input.string().maxLength(100).build()) (3)
.build();
group.setValue(PropertyBox.builder(PROPERTIES).set(ID, 1L).set(DESCRIPTION, "TestDescription").build()); (4)
PropertyBox value = group.getValue(); (5)
group.addValueChangeListener(e -> { (6)
PropertyBox changedValue = e.getValue();
});
1 | Create a PropertyInputGroup using the PROPERTIES property set |
2 | Bind the ID property to a Long type Input field |
3 | Bind the DESCRIPTION property to a String type Input field |
4 | Set the property values using a PropertyBox instance |
5 | Get the property values as a PropertyBox instance |
6 | Add a group ValueChangeListener |
The default Holon platform PropertyRenderer
architecture can be used to automatically generate a suitable Input component for a Property
, according to its type.
The bind(…)
builder method can therefore be used to override the default property input generation only when a custom Input
type is required.
See Property renderers and presenters for further information about property renderers. |
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES).build(); (1)
1 | The property Input fields are automatically generated using the available PropertyRenderer from the default registry |
6.4.1. Read-only properties
A property can be setted as read-only, to prevent the modification of its value. When a property is read-only, the Input
component will be in read-only mode too, preventing the user from changing the value.
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
.readOnly(ID) (1)
.build();
1 | The ID property is setted as read-only, preventing the user from changing the property value |
6.4.2. Hidden properties
A property can be setted as hidden. When a property is hidden, it is part of the group property set and its value will be preserved in the PropertyBox
instances, but no Input
component will be generated and bound to the property itself, so its value will never be visible on the user interface.
The hidden(Property property)
method of the group builder can be used to set a property as hidden.
6.4.3. Default property values
A default value can be provided for a property. If available, the default value will be used as property value when the corresponding Input component is empty (i.e. has no value).
The defaultValue(Property<T> property, DefaultValueProvider<T> defaultValueProvider)
method of the group builder can be used to set a default value for a property.
6.5. Input group validation
A PropertyInputGroup
support both property value and group value validation, using both standard Holon platform Validator
and Vaadin validators.
A set of required(…)
group builder methods are provided to add a required validator to a property (i.e. a validator that check the value is not null or empty) and to show the required indicator symbol alog with the property input component.
When a validator in bound to the overall input group, the value to validate will be represented by the PropertyBox
which contains all the current property values.
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
.withValidator(DESCRIPTION, Validator.max(100)) (1)
.required(ID) (2)
.required(ID, "The ID value is required") (3)
.withValidator(Validator.create(propertyBox -> propertyBox.getValue(ID) > 0,
"The ID value must be greater than 0")) (4)
.build();
1 | Add a validator bound to the DESCRIPTION property to check the value size is not greater than 100 characters |
2 | Add a required validator to the ID property |
3 | Add a required validator to the ID property providing a custom validation error message |
4 | Add an overall validator which read the ID property value from the group PropertyBox and checks it is greater than 0 |
The input group validation is performed invoking all the available property validators, and then the overall group validators, if any.
PropertyInputGroup group = createInputGroup();
group.validate(); (1)
PropertyBox value = group.getValue(); (2)
value = group.getValue(false); (3)
value = group.getValueIfValid().orElse(null); (4)
1 | Explicit group validation: if some validation failure occurs, a ValidationException is thrown |
2 | Using the default getValue() method, the validation is triggered automatically, and a ValidationException is thrown if validation fails |
3 | The getValue(boolean validate) method can be used to skip value validation |
4 | The getValueIfValid() returns an Optional, which contains the PropertyBox value only if the validation succeded |
A ValidationStatusHandler can be used to control how the validation errors are notified to the user, listening to validation status change events.
The property input group support ValidationStatusHandler
configuration both property and overall validation.
Furthermore, the input group builder provides two methods to control if the validation failures are to be collected and all notified to the user or if the validation has to stop at first failure, both from a properties or a group point of view.
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
.stopValidationAtFirstFailure(true) (1)
.stopOverallValidationAtFirstFailure(true) (2)
.validationStatusHandler(validationEvent -> { (3)
// ...
}).propertiesValidationStatusHandler(validationEvent -> { (4)
// ...
}).build();
1 | Set to stop overall validation at first failure |
2 | Set to stop properties validation at first failure |
3 | Set the overall ValidationStatusHandler |
4 | Set the properties ValidationStatusHandler |
A property input group ValidationStatusHandler setter method which accept a Label is provided as a shorter to use a standard Vaadin Label to notify validation errors.
|
6.6. Input Forms
The PropertyInputForm represents a Input
group bound to a Property
model which is also a UI Component
, and can be used to display and manage the Input
components in the UI.
It is a Grouping Inputs, from which inherits all the Input
generation, configuration and management features, using a PropertyBox
to set and get the property values bound to each Input component.
It extends the ComposableComponent interface, which represents an UI component with a base layout and the capability to compose a set of UI components on this layout.
The Composer
interface is used to compose the Input
components on the UI, and must be provided to the PropertyInputForm
, along with the base layout component on which the Input
components will be organized.
The ComposableComponent interface provides the componentContainerComposer() static method to obtain a default composer which uses a Vaadin ComponentContainer as base layout.
|
The inputs composition is triggered using the compose()
method. The PropertyInputForm
builder provides a composeOnAttach(…)
method to set whether to automatically trigger the components composition when the form is attached to a parent layout, and this is the standard behaviour.
Furthermore, a initializer(…)
builder method is made available to perform custom configuration of the base layout component.
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyInputForm form = Components.input.form(new FormLayout()).properties(PROPERTIES).required(ID).build(); (1)
form = Components.input.form(new FormLayout()).properties(PROPERTIES).required(ID)
.composer((layout, source) -> { (2)
source.getValueComponents().forEach(c -> layout.addComponent(c.getComponent()));
}).build();
form.setValue(PropertyBox.builder(PROPERTIES).set(ID, 1L).set(DESCRIPTION, "Test").build()); (3)
PropertyBox value = form.getValue(); (4)
1 | Create a PropertyInputForm with given property set using a FormLayout as base layout, and using the default composer |
2 | Set a custom Composer to compose the Input components on the form layout |
3 | Set the form value using a PropertyBox |
4 | Get the form value |
A set of default builder factory methods are provided to use the most common Vaadin layout components as form base content.
PropertyInputForm form = Components.input.form().properties(PROPERTY_SET).build(); (1)
form = Components.input.formVertical().properties(PROPERTY_SET).build(); (2)
form = Components.input.formHorizontal().properties(PROPERTY_SET).build(); (3)
form = Components.input.formGrid().properties(PROPERTY_SET)
.initializer(gridLayout -> gridLayout.setSpacing(true)).build(); (4)
1 | Create a PropertyInputForm using a FormLayout as base content |
2 | Create a PropertyInputForm using a VerticalLayout as base content |
3 | Create a PropertyInputForm using a HorizontalLayout as base content |
4 | Create a PropertyInputForm using a GridLayout as base content and setting an initializer function to perform GridLayout configuration |
6.7. View components
The ViewComponent represents a UI component which can be used to display a value on the user interface. Unlike a Input
component the value cannot be changed by the user.
As a ValueHolder
, the view component value can be setted and obtained using the setValue
and getValue
methods.
The actual UI component to display is obtained form the getComponent()
method.
A Vaadin Label
is used for the default ViewComponent
implementation, and a default view component can be built using the default builder which can be obtained from the builder(Class valueType)
method or from the Components.view
interface.
ViewComponent<String> view = Components.view.component(String.class)
.caption("TheCaption", "caption.message.code").icon(FontAwesome.CAMERA).styleName("my-style").build(); (1)
view.setValue("TestValue"); (2)
String value = view.getValue(); (3)
1 | Create and configure a ViewComponent with a String value type |
2 | Set the value |
3 | Get the value |
6.7.1. View component Groups and Forms
Just like the Input
components, the ViewComponent
s can be organized in groups (using the PropertyViewGroup) or in forms (using the PropertyViewForm). Unlike the group, the form is a UI component which can be displayed in the user interface, composing the view components on a base layout.
These component containers rely on the Property
model to bind each view component to a Property
value and they use a PropertyBox
to provide the property values to show.
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyViewGroup viewGroup = Components.view.propertyGroup().properties(PROPERTIES).build(); (1)
PropertyViewForm viewForm = Components.view.formVertical().properties(PROPERTIES).build(); (2)
viewForm = Components.view.form(new FormLayout()).properties(PROPERTIES) //
.composer((layout, source) -> { (3)
source.getValueComponents().forEach(c -> layout.addComponent(c.getComponent()));
}).build();
viewForm.setValue(PropertyBox.builder(PROPERTIES).set(ID, 1L).set(DESCRIPTION, "Test").build()); (4)
PropertyBox value = viewForm.getValue(); (5)
1 | Create a PropertyViewGroup |
2 | Create a PropertyViewForm using a VerticalLayout as base layout |
3 | Create a PropertyViewForm using a FormLayout as base layout and providing a custom components composer |
4 | Set the form value using a PropertyBox |
5 | Get the form value |
7. Item listing
The ItemListing component can be used to display a set of items as tabular data. By default, it is backed by a Vaadin Grid
as concrete UI component, but the Vaadin Table
component is supported too.
Each item property to display is bound to a column, and a Property id is used to bind the item property to the listing column.
The ItemListing
is a Selectable component, supporting items selection in single or multiple mode.
The ItemListing
interface provides a set of methods to control the listing appearance, showing and hiding columns, sorting and refreshing the item set, and so on.
Two ItemListing
sub-interfaces are available to use this component:
-
BeanListing: Uses a Java Bean as item type and the listing column ids are the bean property names.
-
PropertyListing: Uses a
PropertyBox
as item type and the listing column ids are theProperty
of the provided property set.
7.1. BeanListing
A BeanListing can be obtained providing the Java Bean class to use as item type.
The listing columns will be generated inspecting the bean class to detect the available bean properties, and the column ids will be the bean property names.
private class TestData {
private Long id;
private String description;
// getters and setters omitted
}
public void beanListing() {
BeanListing<TestData> listing = Components.listing.items(TestData.class) (1)
.header("id", "The ID") (2)
.header("description", "The description") (3)
.build();
listing.refresh(); (4)
listing = Components.listing.itemsUsingTable(TestData.class).build(); (5)
}
1 | Create a BeanListing using the TestData bean class |
2 | Set the column header for the id item (bean) property |
3 | Set the column header for the description item (bean) property |
4 | Refresh the items set |
5 | Create a BeanListing using a Table as backing component |
7.2. PropertyListing
A PropertyListing can be obtained providing the Property
set to use for the listing items.
The listing columns will be generated according to the item property set Property
types and each column will be identified by the Property
itself.
For each property:
-
The property caption, if available, will be used as column header. The caption/header localization is fully supported and performed if a
LocalizationContext
is available; -
The cells of the column which corresponds to a property will be rendered according to the property type and relying on the available property value presenters;
-
For any property of
com.vaadin.ui.Component
type, aComponentRenderer
will be automatically used to render the Vaadin components in the cells.
final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);
PropertyListing listing = Components.listing.properties(PROPERTIES).build(); (1)
listing = Components.listing.propertiesUsingTable(PROPERTIES).build(); (2)
1 | Create a PropertyListing using given Property set |
2 | Create a PropertyListing using a Table as backing component |
7.3. Items data source
To build an item listing, the data source of the items must be provided using the ItemDataProvider interface.
ItemDataProvider<PropertyBox> dataProvider = getDataProvider();
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.dataSource(dataProvider) (1)
.build();
1 | Create a PropertyListing and provide an ItemDataProvider as data source |
Furthermore, an ItemIdentifierProvider should be configured to provide the item identifiers, expecially for the item selection consistency. The default behaviour is to use item itself as its own identifier, which it is not always the right semantic to use, expecially for PropertyBox
type objects.
ItemDataProvider<PropertyBox> dataProvider = getDataProvider();
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.dataSource(dataProvider, item -> item.getValue(ID)) (1)
.build();
listing = Components.listing.properties(PROPERTIES) //
.dataSource(dataProvider, ID) (2)
.build();
1 | Create a PropertyListing and provide an ItemDataProvider as data source, along with an ItemIdentifierProvider which provides the value of the ID property as item id |
2 | A shorter builder method to use the ID property as item identifier |
The item listing builders make available convenience methods to use a Datastore
as data source, providing the DataTarget
to use as target of the query on the backend data (for example, the name of a RDBMS table).
Datastore datastore = getDatastore();
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.dataSource(datastore, DataTarget.named("test"), ID) (1)
.build();
1 | Create a PropertyListing and use a Datastore to build a data source which performs a query using the test data target (which can be, for example, the name of a RDBMS table), the property set of the listing as query projection and the ID property as item identifier |
7.4. Columns configuration
The listing component columns can be configured in a number of ways:
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.header(ID, "Custom ID header") (1)
.columnHidingAllowed(true) (2)
.hidable(ID, false) (3)
.columnReorderingAllowed(true) (4)
.alignment(ID, ColumnAlignment.RIGHT) (5)
.hidden(DESCRIPTION, true) (6)
.resizable(ID, false) (7)
.width(ID, 100) (8)
.expandRatio(DESCRIPTION, 1) (9)
.minWidth(DESCRIPTION, 200) (10)
.maxWidth(DESCRIPTION, 300) (11)
.style(ID, (property, item) -> item.getValue(DESCRIPTION) != null ? "empty" : "not-empty") (12)
.withPropertyReorderListener((properties, userOriginated) -> { (13)
// ...
}).withPropertyResizeListener((property, widthInPixel, userOriginated) -> { (14)
// ...
}).withPropertyVisibilityListener((property, hidden, userOriginated) -> { (15)
// ...
}).build();
1 | Set a custom header caption for the ID property/column |
2 | Set whether the listing columns can be hidden |
3 | Set that the ID property/column cannot be hidden |
4 | Set whether the listing columns can reordered |
5 | Set the ID property/column cell contents alignment |
6 | Set the DESCRIPTION property/column as hidden by default |
7 | Set the ID property/column as not resizable |
8 | Set the ID property/column width in pixels |
9 | Set the DESCRIPTION property/column expand ratio |
10 | Set the DESCRIPTION property/column minimum width |
11 | Set the DESCRIPTION property/column maximum width |
12 | Set the ID property/column CSS style generator |
13 | Add a listener to be notified when the listing columns order changes |
14 | Add a listener to be notified when a property/column is resized |
15 | Add a listener to be notified when a property/column is shown or hidden |
7.5. Column rendering
The default column rendering can be overridden using a custom vaadin Renderer
. The Renderer
must handle the same data type of the property/column for which it is configured.
Furthermore, a property/column value can be converted to a different value type, and, in this case, a suitable Renderer
for the converted type must be provided.
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.render(ID, new NumberRenderer(Locale.US)) (1)
.render(ID, new Converter<String, Long>() { (2)
@Override
public Long convertToModel(String value, Class<? extends Long> targetType, Locale locale)
throws ConversionException {
return Long.valueOf(value);
}
@Override
public String convertToPresentation(Long value, Class<? extends String> targetType, Locale locale)
throws ConversionException {
return (value == null) ? null : String.valueOf(value);
}
@Override
public Class<Long> getModelType() {
return Long.class;
}
@Override
public Class<String> getPresentationType() {
return String.class;
}
}, new TextRenderer()).build();
1 | Set a custom NumberRenderer for the ID property/column (which is of type Long ) |
2 | Set a converter to convert the Long type value of the ID property/column into a String and a suitable render according to the new value type |
7.6. Listing configuration
The item listing component is highly configurable. Through the builders you can configure:
-
The header appeareance, adding and removing header rows and joining header columns
-
Showing and configuring a footer section
-
Set a number of column as frozen
-
Provide rows CSS style using a style generator
-
Provide item description (tooltip) for item rows
-
Provide a item click listener to listen for user clicks on item rows
-
Provide a row details component which can be shown for each item row
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.heightByContents() (1)
.frozenColumns(1) (2)
.hideHeaders() (3)
.withRowStyle(item -> { (4)
return item.getValue(DESCRIPTION) != null ? "has-des" : "no-nes";
}) //
.itemDescriptionGenerator(item -> item.getValue(DESCRIPTION)) (5)
.detailsGenerator(item -> { (6)
VerticalLayout component = new VerticalLayout();
// ...
return component;
}).withItemClickListener((item, property, event) -> { (7)
// ...
}).header(header -> { (8)
header.getDefaultRow().setStyleName("my-header");
}).footer(footer -> { (9)
footer.appendRow().setStyleName("my-footer");
}).footerGenerator((source, footer) -> { (10)
footer.getRowAt(0).getCell(ID).setText("ID footer");
}).withPostProcessor(grid -> { (11)
// ...
}).build();
1 | Set the height of the listing defined by its contents |
2 | Set the frozen columns count |
3 | Hide all the listing headers |
4 | Set the row CSS style generator |
5 | Set the item/row description (tooltip) generator |
6 | Set the item/row details component generator |
7 | Register a item click listener |
8 | Add a style class name to the default header row |
9 | Append a row to the listing footer and set a style class name for it |
10 | Set the footer generator to invoke to update the listing footer contents |
11 | Set a post-processor to customize the internal Grid component |
7.7. Item selection
The item listing component supports items selection, both in single and multiple mode. The listing can be made selectable using the setSelectionMode
method or the selectionMode
builder method.
Just like any other Selectable
component, the item listing component provides methods to inspect the current selected item/s and change the current selection.
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.selectionMode(SelectionMode.SINGLE) (1)
.build();
final PropertyBox ITEM = PropertyBox.builder(PROPERTIES).set(ID, 1L).build();
PropertyBox selected = listing.getFirstSelectedItem().orElse(null); (2)
boolean isSelected = listing.isSelected(ITEM); (3)
listing.select(ITEM); (4)
listing.deselectAll(); (5)
1 | Set the listing selectable in SINGLE selection mode |
2 | Get the currently selected item, if any |
3 | Check whether the given item is selected |
4 | Select the given item |
5 | Deselect all (clear current selection) |
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.build();
listing.setSelectionMode(SelectionMode.MULTI); (1)
final PropertyBox ITEM = PropertyBox.builder(PROPERTIES).set(ID, 1L).build();
Set<PropertyBox> selected = listing.getSelectedItems(); (2)
boolean isSelected = listing.isSelected(ITEM); (3)
listing.select(ITEM); (4)
listing.deselectAll(); (5)
listing.selectAll(); (6)
1 | Set the listing selectable in MULTI selection mode |
2 | Get the selected items |
3 | Check whether the given item is selected |
4 | Select the given item |
5 | Deselect all items |
6 | Select all items |
7.8. Item query configuration
The item listing builder provides a number of methods to control the data source items query, allowing to:
-
Set a property/column as sortable or not and override the default sort behaviour, for example using another property as query sort target or providing a custom
QuerySort
; -
Set a fixed sort, which will always to appended to current query sorts
-
Set a default sort, to be used when no other query sort is configured
-
Set a fixed filter, which will be always applied to the query
-
Register one or more
QueryConfigurationProvider
to provide custom query configuration logic
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.sortable(ID, true) (1)
.sortUsing(ID, DESCRIPTION) (2)
.sortGenerator(ID, (property, ascending) -> { (3)
return ascending ? ID.asc() : ID.desc();
}) //
.fixedSort(ID.asc()) (4)
.defaultSort(DESCRIPTION.asc()) (5)
.fixedFilter(ID.gt(0L)) (6)
.withQueryConfigurationProvider(() -> DESCRIPTION.isNotNull()) (7)
.build();
1 | Set the ID property/column as sortable |
2 | Set to use the DESCRIPTION property to sort the ID property/column |
3 | Set a custom QuerySort for the ID property/column |
4 | Set a fixed QuerySort , which will always to appended to current query sorts |
5 | Set the default QuerySort , to be used when no other query sort is configured |
6 | Set a fixed QueryFilter |
7 | Add a QueryConfigurationProvider |
7.9. Item set management
The item set component provides methods to manage the item set which is provided as the listing data source, to add, update and remove one or more item in the set.
To reflect the item set management operations in the underlyng data source (for example, a RDBMS), the CommitHandler
interface is used. The commit(…)
method of that interface is invoked at each item set modification, providing the added, updated and removed items. The CommitHandler
implementation should persist the item set modifications in the concrete data source.
When using the dataSource(…) builder method which accepts a Datastore as data source, a default CommitHandler is automatically configured, using the provided Datastore to perform the item persistence operations.
|
Datastore datastore = getDatastore();
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.dataSource(datastore, DataTarget.named("test"), ID) (1)
.commitHandler((addedItems, modifiedItems, removedItems) -> { (2)
// ...
}).build();
final PropertyBox ITEM = PropertyBox.builder(PROPERTIES).set(ID, 777L).set(DESCRIPTION, "A description")
.build();
listing.addItem(ITEM); (3)
listing.refreshItem(ITEM); (4)
listing.removeItem(ITEM); (5)
1 | When using the Datastore method of the data source builder, a default CommitHandler is automatically configured |
2 | You can provide a custom CommitHandler using the commitHandler(…) builder method |
3 | Add an item |
4 | Refresh (update) the item |
5 | Remove the item |
7.9.1. Buffered mode
The item set component supports a buffered mode to handle its items set. When in buffered mode, the item set modifications are cached internally and not reflected to the concrete data source until the commit()
method is called.
A discard()
method is provided to discards all changes since last commit.
The same CommitHandler
as before is used to persist the item set modifications, but it is invoked only when the commit()
method is called.
Datastore datastore = getDatastore();
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.dataSource(datastore, DataTarget.named("test"), ID) //
.buffered(true) (1)
.build();
final PropertyBox ITEM = PropertyBox.builder(PROPERTIES).set(ID, 777L).set(DESCRIPTION, "A description")
.build();
listing.addItem(ITEM); (2)
listing.refreshItem(ITEM); (3)
listing.removeItem(ITEM); (4)
listing.commit(); (5)
listing.discard(); (6)
1 | Set the listing in buffered mode |
2 | Add an item |
3 | Refresh (update) the item |
4 | Remove the item |
5 | commit the item set modifications |
6 | discard the item set modifications since the last commit |
7.10. Editing items
The item listing component supports line-based editing, where double-clicking a row opens the row editor, using the default Vaadin Grid
editor. The item listing editor must be enabled using the editable(true)
method of the builder in order to activate the row editor at double-click.
The editor fields to use for each listing property/column will be auto-generated according to the property/column type. This behaviour can be overridden providing custom editor fields for one or more of the the listing properties.
The item listing component editor supports validation, allowing to register value validators both for the single property/column fields and for the overall value validation.
PropertyListing listing = Components.listing.properties(PROPERTIES) //
.editable(true) (1)
.editorSaveCaption("Save item") (2)
.editorCancelCaption("Discard") (3)
.editable(ID, false) (4)
.editor(DESCRIPTION, new TextField()) (5)
.withValidator(DESCRIPTION, Validator.max(100)) (6)
.required(ID) (7)
.build();
1 | Set the listing as editable |
2 | Set the editor save button caption |
3 | Set the editor cancel button caption |
4 | Set a property as not editable |
5 | Set a custom editor field |
6 | Add a property value validator |
7 | Set the ID property as required, automatically adding a not empty validator |
8. Dialogs
The Holon Vaadin module provides builders to create and show dialog windows.
A dialog window in represented by the Dialog interface, which makes available methods to set the dialog as modal, open and close the dialog window and registere a CloseListener
to be invoked when the dialog is closed.
Dialog dialog = Components.dialog() (1)
.draggable(false) (2)
.closable(true) (3)
.resizable(true) (4)
.modal(true) (5)
.message("Dialog message", "dialog.message.code") (6)
.okButtonConfigurator(cfg -> cfg.caption("Done").icon(FontAwesome.CHECK_CIRCLE_O)) (7)
.withCloseListener((window, action) -> { (8)
// ...
}).build();
dialog.open(); (9)
dialog.close(); (10)
1 | Dialog builder |
2 | Set as not draggable |
3 | Set as closable |
4 | Set as resizable |
5 | Set as modal |
6 | Set the localizable dialog message |
7 | Configure the dialog OK button |
8 | Add a close listener |
9 | Open (show) the dialog |
10 | Close (hide) the dialog |
8.1. Question dialogs
A question dialog is a Dialog
with a message and two buttons (instead of one) to answer yes or no to a question.
A QuestionCallback
can be used when the user selects one of the two buttons and the dialog is closed. The callback notifies the selected answer.
Components.questionDialog() (1)
.message("Can I do it for you?") (2)
.yesButtonConfigurator(cfg -> cfg.caption("Ok, let's do it")) (3)
.noButtonConfigurator(cfg -> cfg.caption("No, thanks")) (4)
.callback(answeredYes -> { (5)
Notification.show("Ok selected: " + answeredYes);
}).build().open(); (6)
1 | Question dialog builder |
2 | Set the dialog message (the question) |
3 | Change the yes button caption |
4 | Change the no button caption |
5 | Set the answer callback |
6 | Build and open the dialog |
9. Property renderers and presenters
The Vaadin module takes advantage of the platform foundation property model to provide the most suitable UI components and display values when a Property
is used.
When a Property
must be rendered in UI, the components made available by the Vaadin module try to use a suitable PropertyRenderer
, if available.
By default, the following renderers are automatically registered by the module:
-
A Input property renderer
-
A
com.vaadin.ui.Field
property renderer -
A ViewComponent property renderer
The Input
and Field
property renderers generate a UI component to edit a property value, relying on the property type.
The following types are supported:
-
String: renders the property as a
TextField
-
Boolean: renders the property as a
CheckBox
-
Enum: renders the property as a
ComboBox
-
LocalDate and LocalDateTime: renders the property as a
DateField
or aDateTimeField
-
java.util.Date: renders the property as a
DateField
or aDateTimeField
-
Number: renders the property as a
TextField
which only accepts numbers and decimal/group separators
final PathProperty<String> TEXT = PathProperty.create("text", String.class);
final PathProperty<Long> LONG = PathProperty.create("long", Long.class);
Input<String> input = TEXT.render(Input.class); (1)
Field<Long> field = LONG.render(Field.class); (2)
ViewComponent<String> view = TEXT.render(ViewComponent.class); (3)
1 | Render the property as String type Input |
2 | Render the property as Long type Field |
3 | Render the property as String type ViewComponent |
9.1. Property localization
When a LocalizationContext
is available as a Context resource, it is used to localize any element to display which supports localization. Concerning to a Property
related UI component, it is used to:
-
Localize the property caption, for example when it is used as a listing column header or as a input field caption
-
Obtain the date and time format to use to display a property value or to edit the property value through a a input field
-
Obtain the number format to use to display a property value or to edit the property value through a a input field
-
Obtain the default boolean localized text for the
true
andfalse
values
See the Internationalization documentation for further information about localization. |
9.2. Custom property renderers
Relying on the standard property renderers features, custom renderers can be registered to:
-
Customize the rendered UI component under certain conditions for the default provided rendering types (
Input
,Field
andViewComponent
) -
Provide the rendering for a custom type
For example, to render a specific String
type property as text area, instead of the default text field, an appropriate renderer can be registered and bound to a suitable property condition:
final PathProperty<String> TEXT = PathProperty.create("text", String.class);
InputPropertyRenderer<String> textAreaInputRenderer = p -> Components.input.string(true).build(); (1)
PropertyRendererRegistry.get().register(p -> p == TEXT, textAreaInputRenderer); (2)
Input<String> input = TEXT.render(Input.class); (3)
InputFieldPropertyRenderer<String> fieldRenderer = p -> new TextArea(); (4)
1 | Create a Input property renderer which renders the property as a text area |
2 | Register the renderer and bind it to a condition which checks the property is exactly the TEXT property |
3 | From now on, rendering the TEXT property as an Input field will generate a text area input component |
4 | Example of a Input property renderer which renders the property as a text area using a Vaadin Field |
10. Vaadin session scope
A Vaadin session platform scope is automatically registered by the Holon Vaadin module.
This ContextScope
is bound to the current VaadinSession
, looking up context resources using Vaadin Session attributes.
The scope uses VaadinSession.getCurrent()
to obtain the current Vaadin session, so this scope is active only when invoked within a Vaadin server request thread.
See the Context documentation for further information about context scopes and resources. |
VaadinSession.getCurrent().setAttribute(LocalizationContext.CONTEXT_KEY,
LocalizationContext.builder().withInitialLocale(Locale.US).build()); (1)
LocalizationContext.getCurrent().ifPresent(localizationContext -> { (2)
// LocalizationContext obtained from current Vaadin session
});
1 | Create a LocalizationContext and set it in Vaadin session using the default context key as attribute name |
2 | Get the current LocalizationContext context resource, which will be obtained form the Vaadin session, if available |
11. Device information
The Holon platform Vaadin module makes available the DeviceInfo interface, to obtain informations about the device in which the application is running.
The DeviceInfo
object provides informations about the user agent and screen dimensions, and can be obtained using a VaadinRequest
or relying on the currently available VaadinSession
.
DeviceInfo.get().ifPresent(info -> {
info.getScreenWidth();
info.getScreenHeight();
info.getViewPortWidth();
info.getViewPortHeight();
info.isMobile();
info.isTablet();
info.isSmartphone();
info.isAndroid();
info.isIPhone();
// ...
});
12. Navigator
The holon-vaadin-navigator
artifact makes available an extension of the default view navigator, represented by the ViewNavigator interface, which provides additional features and configuration capabilities:
-
View
navigation history tracking, allowing to navigate back to the previous view -
View
parameters management, allowing to obtain the parameters values by injection in theView
instance -
Easy
View
lifecycle management, supportingOnShow
andOnLeave
annotated methods to be notified when the view is loaded and unloaded in the UI -
Default view support, acting as the application’s "home page"
-
The possibility to show a view in a
Window
, instead of using the default view display component -
Support for context resources injection in
View
instances
Maven coordinates:
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-vaadin-navigator</artifactId>
<version>5.2.4</version>
12.1. View configuration
12.1.1. Parameters injection
The navigation parameters can be injected in the View
class fields using the ViewParameter annotation.
The parameter name for which the value has to be injected in an annotated field is assumed to be equal to the field name. Otherwise, it can be specified using the annotation value()
property.
When a named parameter value is not available (not provided by the current navigation state), the parameter field will be setted to null
or the default value for primitive types (for example 0
for int
, long
, float
and double
types, false for boolean
types).
The supported parameter value types are:
-
String
-
Number
s (including primitive types) -
Boolean
(including primitive type) -
Enum
-
Date
-
LocalDate
-
LocalTime
-
LocalDateTime
A view parameter can be declared as required using the required()
annotation property. A navigation error will be thrown if a required parameter value is missing.
Furthermore, a default value can be provided for a parameter, using the defaultValue()
annotation property. See the ViewParameter javadocs for information about the default value representation.
class ViewExample implements View {
@ViewParameter("myparam") (1)
private String stringParam;
@ViewParameter(defaultValue = "1") (2)
private Integer intParam;
@ViewParameter(required = true) (3)
private LocalDate requiredParam;
@Override
public void enter(ViewChangeEvent event) {
}
}
1 | View parameter injection using the myparam parameter name |
2 | View parameter with a default value. The parameter name will be the intParam field name |
3 | A required view parameter declaration |
12.1.2. View lifecycle hooks
In addition to the standard enter(…)
method of the View
interface, two annotations can be used to intercept View
lifecycle events using public
View methods:
If more than one OnShow
or OnLeave
annotated method in present in the View
class or in it’s class hierarchy, all these methods will be called and the following behaviour will be adopted:
-
Methods will be called following the class hierarchy, starting from the top (the first superclass after
Object
) -
For methods of the same class, no calling order is guaranteed
The OnShow
and OnLeave
annotated method supports an optional parameter, which can be either of the standard com.vaadin.navigator.ViewChangeEvent
type or of the extended ViewNavigatorChangeEvent
type, to obtain informations about the current view navigator, the previous or next View
, the View
name and parameters and the optional Window
in which the View is displayed.
The OnShow
annotation provides an additional onRefresh()
property which, if setted to true
, instruct the navigator to invoke the OnShow
annotated method also at browser page refresh.
class ViewExample2 implements View {
@ViewParameter
private String myparam;
@OnShow
public void onShow() { (1)
// ...
}
@OnShow(onRefresh = true) (2)
public void onShowAtRefreshToo() {
// ...
}
@OnShow
public void onShow2(ViewChangeEvent event) { (3)
String name = event.getViewName(); (4)
View oldView = event.getOldView(); (5)
// ...
}
@OnShow
public void onShow3(ViewNavigatorChangeEvent event) { (6)
ViewNavigator navigator = event.getViewNavigator(); (7)
Optional<Window> viewWindow = event.getWindow(); (8)
// ...
}
@OnLeave
public void onLeave() { (9)
// ...
}
@OnLeave
public void onLeave2(ViewNavigatorChangeEvent event) { (10)
View nextView = event.getNewView(); (11)
// ...
}
@Override
public void enter(ViewChangeEvent event) {
}
}
1 | Basic OnShow annotated method, invoked right before the view is shown |
2 | OnShow method with onRefresh() setted to true : this method will be invoked also at browser page refresh |
3 | OnShow method with a default ViewChangeEvent parameter |
4 | Get the View name |
5 | Get the previous View |
6 | OnShow method with a ViewNavigatorChangeEvent parameter |
7 | Get the ViewNavigator |
8 | Get the Window in which the view is displayed if it was requested to open the View in a window |
9 | Basic onLeave annotated method |
10 | onLeave method with a ViewNavigatorChangeEvent parameter |
11 | Get the View being activated |
12.1.3. Providing the View contents
By default, the View
is rendered as the UI component which is represented by the View
class.
The ViewContentProvider interface can be implemented by a View
class to control which UI content to provide through the getViewContent()
method. The method is called by the ViewNavigator
to display the View
when it is activated.
class ViewExampleContent extends VerticalLayout implements View { (1)
public ViewExampleContent() {
super();
addComponent(new Label("View content"));
}
@Override
public void enter(ViewChangeEvent event) {
}
}
class ViewExampleContentProvider implements View, ViewContentProvider { (2)
@Override
public Component getViewContent() { (3)
boolean mobile = DeviceInfo.get().map(info -> info.isMobile()).orElse(false);
return mobile ? buildMobileViewContent() : buildDefaultViewContent();
}
@Override
public void enter(ViewChangeEvent event) {
}
}
1 | Default View implementation extending a Component class (a VerticalLayout ). The displayed View content will be the component itself |
2 | A View implementing the ViewContentProvider interface: the displayed UI content will be provided by the getViewContent() method |
3 | Provide the View content. In this example, a different UI component is provided according to the device type, checking if the user is running the application in a mobile device |
12.2. Navigator Configuration
Just like the standard Vaadin navigator, the ViewNavigator
requires two elements which must be configured in order to work properly:
-
A
ViewProvider
to provide thView
instances by view name -
A
View
display component to show the view contents in the UI, which can be aComponentContainer
(replacing the contents of the container with the activeView
content), aSingleComponentContainer
(using thesetContent(…)
method to set the activeView
content) or aViewDisplay
object to implement a customView
display logic.
In addition to the required configuration elements, the ViewNavigator
supports:
-
Setting a default view name, which will be used as target of the
ViewNavigator.navigateToDefault()
method and as a fallback by theViewNavigator.navigateBack()
method if no otherView
is available in navigation history -
Setting a error view or a error
ViewProvider
to provide theView
to be displayed when no otherView
matches a navigation state -
Setting the max navigation history size
-
Register a
ViewChangeListener
for listening toView
changes before and after they occur
The builder()
method of the ViewNavigator
interface provides a fluent builder to configure a navigator and bind it to a Vaadin UI
.
class AppUI extends UI {
@Override
protected void init(VaadinRequest request) {
ViewNavigator navigator = ViewNavigator.builder() (1)
.viewDisplay(this) (2)
.addProvider(getViewProvider()) (3)
.defaultViewName("home") (4)
.errorView(MY_ERROR_VIEW) (5)
.errorViewProvider(getErrorViewProvider()) (6)
.maxNavigationHistorySize(1000) (7)
.navigateToDefaultViewWhenViewNotAvailable(true) (8)
.withViewChangeListener(new ViewChangeListener() { (9)
@Override
public boolean beforeViewChange(ViewChangeEvent event) {
// ...
return true;
}
@Override
public void afterViewChange(ViewChangeEvent event) {
// ...
}
}).buildAndBind(this); (10)
}
}
1 | Obtain the ViewNavigator builder |
2 | Set the component to which the View contents display is delegated |
3 | Add a ViewProvider |
4 | Set the default View name |
5 | Set the error View |
6 | Set the error View provider |
7 | Set the max navigation history size |
8 | Configure the navigator to navigate to default View (if availablr) when a view is not available according to current navigation state |
9 | Add a ViewChangeListener |
10 | Build the ViewNavigator and bind it to the UI |
The ViewNavigator
builder makes available a default View
provider, which supports a fixed set of Views
configured using a View name bound to a View class.
Stateful views are supported by this provider. To declare a View as stateful, the StatefulView annotation can be used on the View class.
View instances will be created according to view scope: for stateful views, an instance is created at first request (for each UI) and the same instance is returned to subsequent view requests. On the contrary, for standard views, a new instance is created and returned to navigator for every view request.
To register this view provider and configure the View
set, the ViewNavigator
builder method withView(String viewName, Class<? extends View> viewClass)
can be used.
UI ui = getUI();
ViewNavigator.builder() //
.viewDisplay(ui) //
.withView("view1", View1.class) (1)
.withView("view2", View2.class) (2)
.defaultViewName("view1") (3)
.buildAndBind(ui);
1 | Register the view class View1 and bind it the view1 name |
2 | Register the view class View2 and bind it the view2 name |
3 | Set the view1 View as the default view |
12.3. Excluding a View form navigation history
By default, the ViewNavigator
tacks views navigation history, to made available navigation operations such navigateBack()
to navigate to the previous view.
To exclude a View
from the navigation history, skipping such View when a navigateBack()
operation is performed or the user presses the browser back button, the VolatileView annotation can be used on the View
class.
@VolatileView
class VolatileViewExample implements View {
@Override
public void enter(ViewChangeEvent event) {
}
}
12.4. Opening Views in a Window
The ViewNavigator
supports displaying a View
content in a application Window
. The view navigation is tracked in navigator history just like any other navigation operation.
To open a View
in a Window
, the navigateInWindow(…)
view navigator methods can be used (see the next section), or a View
class can be forced to be always opened in a window using the WindowView annotation.
If the @WindowView
annotation is found on a view class, such view will be always opened in a window, regardless of the navigation operation that is used.
The Window
is automatically created by the ViewNavigator
, and can be configured using either a ViewWindowConfigurator
, when the navigator method is used, or the @WindowView
annotation attributes.
@WindowView(windowWidth = "50%")
class WindowViewExample implements View {
@Override
public void enter(ViewChangeEvent event) {
}
}
12.5. View navigation operations
The ViewNavigator
API provides navigation methods to provide a set of parameters to the View
being activated.
These parameters are serialized in the navigation state String, and are automatically bound to the any View
class field annotated with the @ViewParameter
annotation.
See Parameters injection for further informations about View
parameters injection.
When using @ViewParameter annotated View methods, the parameter value types provided to a view navigation method through the name-value Map must be consistent with the corresponding @ViewParameter view class field type.
|
The ViewNavigator
API provides the following operations for View
navigation:
-
navigateTo(String viewName, Map<String, Object> parameters)
: navigate to givenView
name, providing an optional Map of parameters with parameters names and corresponding values. -
navigateTo(String viewName)
: navigate to givenView
name without providing any view parameter.
ViewNavigator navigator = getViewNavigator();
navigator.navigateTo("myView"); (1)
Map<String, Object> parameters = new HashMap<>();
parameters.put("parameter1", "test");
parameters.put("parameter2", 34.5);
navigator.navigateTo("myView", parameters); (2)
1 | Navigate to the view named myView |
2 | Navigate to the view named myView providing a map of parameters (names and values)
|
ViewNavigator navigator = getViewNavigator();
navigator.navigateInWindow("myView"); (1)
Map<String, Object> parameters = new HashMap<>();
parameters.put("parameter1", "test");
parameters.put("parameter2", 34.5);
navigator.navigateInWindow("myView", windowConfig -> {
windowConfig.fullWidth();
windowConfig.styleName("my-window-style");
}, parameters); (2)
1 | Navigate to the view named myView and display the view in a Window |
2 | Navigate to the view named myView and display the view in a Window, providing a window configuration consumer and a map of parameters
|
ViewNavigator navigator = getViewNavigator();
navigator.toView("myView").withParameter("parameter1", "test").withParameter("parameter2", 34.5).navigate(); (1)
navigator.toView("myView").navigateInWindow(); (2)
navigator.toView("myView").navigateInWindow(windowConfig -> {
windowConfig.fullWidth();
windowConfig.styleName("my-window-style");
}); (3)
1 | Navigate to the view named myView using given parameters |
2 | Navigate to the view named myView and display the view in a Window |
3 | Navigate to the view named myView and display the view in a Window, providing a window configuration consumer
|
12.6. Sub views
The ViewNavigator
supports the concept of sub view: a sub view is a View
which is always displayed using a parent view container, and the sub view display strategy is delegated to the container.
A sub view container is a conventional View
which implements the SubViewOf interface.
Any sub view must declare its parent container view name using the WindowView annotation.
When the navigation to a sub view is triggered, the ViewNavigator
first of all will navigate to the declared sub view container, displaying it as a normal View
, than the display(View view, String viewName, Map<String, String> parameters)
method of the sub view container is invoked providing the sub view instance, the sub view name and any navigation parameter. At this point, the container should display the sub view contents in a suitable way.
The getViewContent(View view) static method of the ViewNavigator interface can be used to obtain the view contents from a View instance.
|
class SubViewContainerExample extends TabSheet implements SubViewContainer { (1)
public SubViewContainerExample() {
super();
setSizeFull();
}
@Override
public boolean display(View view, String viewName, Map<String, String> parameters)
throws ViewNavigationException {
addTab(ViewNavigator.getViewContent(view), viewName, FontAwesome.PUZZLE_PIECE);
return true;
}
@Override
public View getCurrentView() {
return (View) getSelectedTab();
}
@Override
public void enter(ViewChangeEvent event) {
}
}
@SubViewOf("mycontainer")
public class SubViewExample extends VerticalLayout implements View { (2)
public SubViewExample() {
super();
setMargin(true);
addComponent(new Label("The sub view 1"));
}
@Override
public void enter(ViewChangeEvent event) {
}
}
1 | A view declared as SubViewContainer using a TabSheet to display each sub view in a new tab. We suppose this view is bound to the mycontainer view name |
2 | A sub view bound to the mycontainer sub view container using the @SubViewOf annotation |
12.7. Context resources injection
The ViewNavigator
supports Context resources injection into the View
instances. A resource available form the Holon platform Context
registry can be injected in a View
using the ViewContext annotation on a View
field.
The resource to be injected is looked up by key, and by the default the resource key is assumed to be the fully qualified class name of the injectable field type. To override this strategy, the value()
annotation attribute of the ViewContext
annotation can be used to provide the resource key to look up.
See the Context documentation for further information about context resources. |
class ViewContextExample implements View {
@ViewContext
private LocalizationContext localizationContext;
@ViewContext
private AuthContext authContext;
@Override
public void enter(ViewChangeEvent event) {
}
}
12.8. Obtain the ViewNavigator
The ViewNavigator
interface provides methods to obtain the current navigator using the following strategy:
-
If the
ViewNavigator
is available as Context resource using the default navigator resource key, that instance is returned; -
If a current Vaadin
UI
is available and aViewNavigator
is bound to theUI
, that instance is returned.
See the Context documentation for further information about context resources. |
Optional<ViewNavigator> navigator = ViewNavigator.getCurrent(); (1)
ViewNavigator viewNavigator = ViewNavigator.require(); (2)
1 | Try to obtain the ViewNavigator from context or current UI |
2 | Obtain the ViewNavigator from context or current UI , failing with an exception if not found |
12.9. Authentication support
The ViewNavigator
architecture provides support for View
authentication, relying on the default Holon platform AuthContext
API.
See the Realm and AuthContext documentation for information about the Holon platform authentication and authorization architecture. |
In order for the authentication to work, an AuthContext
instance must be available as a context resource, and it will be used to perform user authentication and resource access control, relying on the Realm
bound to the auth context.
The authentication support is enabled by default, but it can be configured using the authenticationEnabled(…)
ViewNavigator
builder method.
The authentication support is enabled through the standard com.holonplatform.auth.annotations.Authenticate
annotation, which can be used on a View
class or on the application UI
class.
When the Authenticate
annotation is used at UI
level, all the views managed by the navigator bound to such UI
will be under authentication, and the access to any View
will be denied if an authenticated subject is not available from the current AuthContext
.
Each time the navigation to a protected View
is requested, the AuthContext
is checked, and if it not authenticated, the following strategy is implemented:
-
An implicit authentication attempt is performed, using the current
VaadinRequest
and the optional authentication schemes which can be specified using theschemes()
attribute of theAuthenticate
annotation. This behaviour can be used, for example, to support authentication using the current HTTP request and schemes such theAuthorization
HTTP header. -
If the implicit authentication is not successfull and a valid redirect URI is provided through the
redirectURI()
property of theAuthenticate
annotation, the navigation is redirected to that URI. If the redirect URI does not specify a scheme, or the scheme is equal to the specialview://
scheme, the navigation is redirected to the navigation state specified by the redirect URI (escluding theview://
part, if present). This way, the redirect URI can be used to delegate to aView
an explicit authentication entry point (for example using conventional username and password credentials).
13. Spring integration
The holon-vaadin-spring
artifact provides support and integration with the Spring framework.
Maven coordinates:
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-vaadin-spring</artifactId>
<version>5.2.4</version>
This artifact provides a ViewNavigator
extension with Spring support, represented by the SpringViewNavigator interface.
The SpringViewNavigator
implementation relies upon the standard Vaadin Spring integration add-on, and supports all its functionalities and configuration features.
See the Vaadin Spring tutorial for the documentation.
The following annotations are available for View
configuration:
-
DefaultView: can be used on a
View
class to declare it as the default view, i.e the view which will be used as target of theViewNavigator.navigateToDefault()
method and as a fallback by theViewNavigator.navigateBack()
method if no otherView
is available in navigation history. -
ErrorView: can be used on a
View
class to declare it as the default error view, i.e. theView
to be displayed when no otherView
matches a navigation state.
13.1. Spring view navigator configuration
The SpringViewNavigator
API provides a builder to create a navigator instance, and can be used to explicitly build and configure a SpringViewNavigator
instance. The bulder can be obtained through the static builder()
method of the SpringViewNavigator
interface.
The easiest way to setup a Spring view navigator, is to use the provided EnableViewNavigator configuration annotation.
The @EnableViewNavigator
can be used on Spring configuration classes to automatically setup the default Vaadin Spring integration and registering a UI-scoped SpringViewNavigator
bean. The standard @SpringViewDisplay
annotation can be used to configure the views display component and the default Vaadin Spring ViewProvider
will be used.
The @EnableViewNavigator annotation includes the standard com.vaadin.spring.annotation.@EnableVaadin annotation behaviour, which is not required anymore on configuration classes.
|
The @EnableViewNavigator
annotation makes available a number of properties to control the navigator configuration, for example to explicitly configure the default and error views or to set the max navigation history size. See the EnableViewNavigator javadocs for further information.
@Configuration (1)
@ComponentScan(basePackageClasses = ViewOne.class) (2)
@EnableViewNavigator (3)
@EnableBeanContext (4)
class SpringConfig {
}
@SpringView(name = "view1") (5)
@DefaultView (6)
class ViewOne extends VerticalLayout implements View {
@Override
public void enter(ViewChangeEvent event) {
}
}
@SpringView(name = "view2") (7)
@UIScope (8)
class ViewTwo extends VerticalLayout implements View {
@Override
public void enter(ViewChangeEvent event) {
}
}
@SpringUI (9)
@SpringViewDisplay (10)
class AppUI extends UI {
@Autowired
ViewNavigator navigator; (11)
@Override
protected void init(VaadinRequest request) {
// ...
}
}
1 | Declare the class as a Spring configuration class |
2 | Set the component scan rule to auto detect the View beans |
3 | Enable the Spring ViewNavigator |
4 | Enable the Holon platform Spring context scope, to provide context resource instances as Spring beans |
5 | Declare the view as Spring view (which will be automatically registered in the navigator view provider), and bind it to the view1 name |
6 | Declare the view as the default view |
7 | Create another view and enable it as a Spring view using the view2 name |
8 | Declare the view bean scope as UI |
9 | Create the application UI and declare it as a Spring UI , which will be automatically detected and configured by Spring |
10 | Use the UI as View display container |
11 | The ViewNavigator will be made avaialable as Spring (UI-scoped) bean, so it can be obtained using dependency injection |
13.2. View context resources
The EnableViewContext annotation can be used on Spring configuration classes to enable View
context resource injection using the ViewContext annotation.
See Context resources injection for further information.
13.3. View authorization support
In addition to the ViewNavigator
authentication support (see Authentication support), the Spring view navigator provides View authorization support using default javax.annotation.security.*
annotations (@RolesAllowed
, @PermitAll
, @DenyAll
).
The authorization support can be enabled using the EnableViewAuthorization annotation and, just like the authentication support, relies on the current AuthContext
to perform authorization control, so it must be available as a context resource.
By using the @EnableBeanContext configuration annotation, Spring beans can be automatically configured as context resources. See the SpringContextScope documentation for further information.
|
The default Vaadin Spring ViewAccessControl and ViewInstanceAccessControl view access control methods are fully supported too, and can be used along with the security annotations.
|
The AccessDeniedView annotation can be used on a Spring View
class to declare it as the view to show when the user is not authorized to access a view, either according to a javax.annotation.security.*
annotation or to a ViewAccessControl
or ViewInstanceAccessControl
rule.
@Configuration
@ComponentScan(basePackageClasses = ViewOne.class)
@EnableViewNavigator
@EnableBeanContext (1)
@EnableViewAuthorization (2)
class SpringConfig {
@Bean (3)
@VaadinSessionScope
public AuthContext authContext() {
AccountProvider ap = id -> {
// Only a user with username 'username1' is available
if ("username1".equals(id)) {
// setup the user password and assign the role 'role1'
return Optional.of(Account.builder(id).credentials(Credentials.builder().secret("s3cr3t").build())
.withPermission("role1").build());
}
return Optional.empty();
};
return AuthContext.create(Realm.builder()
// authenticator using the AccountProvider
.withAuthenticator(Account.authenticator(ap))
// default authorizer
.withDefaultAuthorizer().build());
}
}
@SpringView(name = "view1")
@PermitAll (4)
class ViewOne extends VerticalLayout implements View {
@Override
public void enter(ViewChangeEvent event) {
}
}
@SpringView(name = "view2")
@RolesAllowed("role1") (5)
class ViewTwo extends VerticalLayout implements View {
@Override
public void enter(ViewChangeEvent event) {
}
}
@AccessDeniedView (6)
@UIScope
@SpringView(name = "forbidden")
class AccessDenied extends VerticalLayout implements View {
private static final long serialVersionUID = 1L;
private final Label message;
public AccessDenied() {
super();
Components.configure(this).margin()
.add(message = Components.label().styleName(ValoTheme.LABEL_FAILURE).build());
}
@Override
public void enter(ViewChangeEvent event) {
message.setValue("Access denied [" + event.getViewName() + "]");
}
}
1 | Use @EnableBeanContext to enable Spring beans as context resources (in this example, the AuthContext bean will be available as context resource) |
2 | Enable views authorization using javax.annotation.security.* annotations |
3 | Configure the AuthContext and declare it as a session-scoped Spring bean |
4 | Use @PermitAll on this view to skip authorization control |
5 | Use @RolesAllowed to declare that the view is available only for the authenticated subjects with the role1 role |
6 | Create a custom access denied view using the @AccessDeniedView annotation |
14. Spring Boot integration
The holon-vaadin-spring-boot
artifact provides integration with Spring Boot for Vaadin application and view navigator auto configuration.
To enable Spring Boot auto-configuration the following artifact must be included in your project dependencies:
Maven coordinates:
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-vaadin-spring-boot</artifactId>
<version>5.2.4</version>
The Spring Boot auto-configuration includes the default Spring Boot Vaadin add-on auto configuration features, with the following additional behaviour:
-
The configured view navigator will be a Spring
ViewNavigator
-
The
View
authorization support using thejavax.annotation.security.*
annotations is enabled by default
To disable this auto-configuration feature the HolonVaadinAutoConfiguration
class can be excluded:
@EnableAutoConfiguration(exclude={HolonVaadinAutoConfiguration.class})
14.1. Spring Boot starters
The following starter artifacts are available to provide a quick project configuration setup using Maven dependency system:
1. The Holon Vaadin application starter provides the dependencies to the Holon Vaadin Spring Boot integration artifact, in addition to default Holon core Spring Boot starters, the default spring-boot-starter-web
starter and the spring-boot-starter-tomcat
to use Tomcat as the embedded servlet container:
Maven coordinates:
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-starter-vaadin</artifactId>
<version>5.2.4</version>
2. The Holon Vaadin application starter using Undertow, to use Undertow instead of Tomcat as embedded servlet container:
Maven coordinates:
<groupId>com.holon-platform.vaadin7</groupId>
<artifactId>holon-starter-vaadin-undertow</artifactId>
<version>5.2.4</version>
15. Loggers
By default, the Holon platform uses the SLF4J API for logging. The use of SLF4J is optional: it is enabled when the presence of SLF4J is detected in the classpath. Otherwise, logging will fall back to JUL (java.util.logging
).
The logger name for the Vaadin module is com.holonplatform.vaadin
.