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 for Input components and Property renderers, while the Vaadin 8 module relies on the HasValue interface

  • The Vaadin 7 Table component is available as ItemListing backing component, in addition to the Grid 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.0</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.0</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.

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.0</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.

Table 1. Components builders structure
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 Label, Button, and standard layout components.

input

Builders for different data type Input components, including selection components. Furthermore, provides builder to create input forms.

view

Builders for ViewComponent components and view forms.

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 example
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 example
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

com.vaadin.ui.NativeSelect

not supported

SELECT

com.vaadin.ui.ComboBox

com.vaadin.ui.ListSelect

OPTIONS

com.vaadin.ui.OptionGroup

com.vaadin.ui.OptionGroup

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(…​) or items(…​) 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 the Property 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, a ComponentRenderer 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.

Single selection mode
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)
Multiple selection mode
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 a DateTimeField

  • java.util.Date: renders the property as a DateField or a DateTimeField

  • 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 and false 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 and ViewComponent)

  • 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();
  // ...

});

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 the View instance

  • Easy View lifecycle management, supporting OnShow and OnLeave 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.0</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:

  • OnShow, called by the view navigator right before the view is shown (i.e. rendered in target display component)

  • OnLeave, called by the view navigator when the view is about to be deactivated (i.e. a navigation to another view was triggered)

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

Just like the standard Vaadin navigator, the ViewNavigator requires two elements which must be configured in order to work properly:

  • A ViewProvider to provide th View instances by view name

  • A View display component to show the view contents in the UI, which can be a ComponentContainer (replacing the contents of the container with the active View content), a SingleComponentContainer (using the setContent(…​) method to set the active View content) or a ViewDisplay object to implement a custom View 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 the ViewNavigator.navigateBack() method if no other View is available in navigation history

  • Setting a error view or a error ViewProvider to provide the View to be displayed when no other View matches a navigation state

  • Setting the max navigation history size

  • Register a ViewChangeListener for listening to View 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 given View name, providing an optional Map of parameters with parameters names and corresponding values.

  • navigateTo(String viewName): navigate to given View 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)
  • navigateInWindow(…​): View navigation methods that force the View content to be displayed in an Window, supporting a ViewWindowConfiguration Consumer function to setup the view Window features.

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
  • Using the NavigationBuilder through the toView(String viewName) method, which accepts the view name and the optional view parameters using a fluent builder style.

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
  • The navigateToDefault() method can be used to navigate to the default view, if configured.

  • The navigateBack() method can be used to navigate back to previous View, if any. In no previous View is available and a default view is defined, navigator will navigate to the default view.

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 a ViewNavigator is bound to the UI, 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:

  1. An implicit authentication attempt is performed, using the current VaadinRequest and the optional authentication schemes which can be specified using the schemes() attribute of the Authenticate annotation. This behaviour can be used, for example, to support authentication using the current HTTP request and schemes such the Authorization HTTP header.

  2. If the implicit authentication is not successfull and a valid redirect URI is provided through the redirectURI() property of the Authenticate annotation, the navigation is redirected to that URI. If the redirect URI does not specify a scheme, or the scheme is equal to the special view:// scheme, the navigation is redirected to the navigation state specified by the redirect URI (escluding the view:// part, if present). This way, the redirect URI can be used to delegate to a View 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.0</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 the ViewNavigator.navigateToDefault() method and as a fallback by the ViewNavigator.navigateBack() method if no other View is available in navigation history.

  • ErrorView: can be used on a View class to declare it as the default error view, i.e. the View to be displayed when no other View 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.0</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 the javax.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.0</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.0</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.

16. System requirements

16.1. Java

The Holon Platform Vaadin module requires Java 8 or higher.

16.2. Vaadin

The Holon Platform Vaadin module requires Vaadin 7.7 or higher.