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 module represents the platform support for the Vaadin web applications framework, focusing on the user interface components and data binding features.

This module provides integration with the platform foundation architecture, such as;

This module provides a view navigation system which allows to create web applications focusing on the application functions, which can be represented by virtual pages, relying on a robust navigation architecture, with parameters and view lifecycle hooks support.

Furthermore, a complete set of fluent builders is available, to build web application UI components quickly and easily.

A complete integration with the Spring framework and Spring Boot is also available.

1.1. Sources and contributions

The Holon Platform Vaadin module source code is available from the GitHub repository https://github.com/holon-platform/holon-vaadin.

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.vaadin</groupId>
<artifactId>holon-vaadin-bom</artifactId>
<version>5.2.5</version>

The BOM can be imported in a Maven project in the following way:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.holon-platform.vaadin</groupId>
      <artifactId>holon-vaadin-bom</artifactId>
      <version>5.2.5</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. What’s new in version 5.1.x

  • Support for the the PropertySet identifier properties to make more easy and quick to setup data bound UI objects. See Items type and identifiers.

  • Full support of Vaadin version 8.3, including for example the Navigator @PushStateNavigation strategy support.

  • Better support for bean based data bound UI objects, with new builders and a Java API to create and configure the UI components just like the Property based one. See BeanListing.

  • Support for components drag and drop configuration at Java API level. See Drag and Drop support.

  • New component builder APIs for Vaadin TabSheet and Accordion components. See Component builders.

4.1. Migrating from version 5.0.x

4.1.1. Deprecations

  • PropertyListing builder API: dataSource(ItemDataProvider dataProvider, Property…​ identifierProperties). Since the PropertySet identifier properties support is used by default by the PropertyListing builder to obtain the item identifier properties, this method is no longer useful. To customize the item identifiers strategy, the more general dataSource(ItemDataProvider dataProvider, ItemIdentifierProvider itemIdentifierProvider) builder method can be used.

4.1.2. Item identifiers in Property based Item listings

The Property based Item listing components now supports the PropertySet identifier properties configuration and use them as the default strategy to identify a PropertyBox type item.

So, when the PropertySet used with an item listing component declares one ore more identifier properties, there is no longer the need to declare the item identifier properties, for example, when configuring a Datastore based item data source. The identifier properties of the PropertySet will be used by default to provide the item identifier values.

For example, given the following Property model definition:

private final static PathProperty<Long> ID = PathProperty.create("id", Long.class);
private final static PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);

private final static PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);

A PropertyListing component, prior to version 5.1.0, had to be built this way, for example using a Datastore as item data source:

Datastore datastore = getDatastore();

PropertyListing listing = Components.listing.properties(PROPERTIES) (1)
    .dataSource(datastore, DataTarget.named("test"), ID) (2)
    .build();
1 Provide the item listing PropertySet
2 Configure a Datastore as item data source, providing the ID property as item identifier property

From version 5.1.x, the Property model definition can include the PropertySet identifier properties like this:

private final static PathProperty<Long> ID = PathProperty.create("id", Long.class);
private final static PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);

private final static PropertySet<?> PROPERTIES = PropertySet.builderOf(ID, DESCRIPTION).identifier(ID).build(); (1)
1 Set the ID property as property set identifier property

And so the item identifier properties to use are no longer required during the PropertyListing component data source configuration, since the PropertySet identifier properties are used by default:

Datastore datastore = getDatastore();

PropertyListing listing = Components.listing.properties(PROPERTIES) (1)
    .dataSource(datastore, DataTarget.named("test")) (2)
    .build();
1 The item listing PropertySet provides the property set identifier properties
2 There is no longer the need to declare the ID property as item identifier property at data source configuration time, since the configured PropertySet identifier properties will be used by default

5. Vaadin UI integration

The holon-vaadin artifact is the main entry point to use the Holon platform Vaadin integration.

Maven coordinates:

<groupId>com.holon-platform.vaadin</groupId>
<artifactId>holon-vaadin</artifactId>
<version>5.2.5</version>

6. Component builders

A complete set of fluent builders is available to create and configure the most common Vaadin components and the additional components provided by the Holon platform Vaadin module.

See UI Components for a list of the additional components made available by the Holon platform Vaadin module.

All the builders can be obtained by using the Components API, which is organized in sub-APIs by components category.

The available component categories are the following:

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, it 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.

7. Component builders common features

The Holon Platform Vaadin component builders make available a set of common features, partly related to standard Vaadin features configuration and partly dedicated to the integration with some core Holon Platform APIs.

For each UI component which extends a standard Vaadin Component hierarchy, the builder APIs provide methods to configure all the component attributes and register the supported listeners. In addition to the common component configuration attributes, each component builder makes available methods to configure the component specific attributes and supported behaviors.

Label builder example
Label lbl = Components.label() (1)
    .fullWidth() (2)
    .height(50, Unit.PIXELS) (3)
    .styleName("my-style").styleName(ValoTheme.LABEL_BOLD) (4)
    .icon(VaadinIcons.CHECK) (5)
    .caption("The caption") (6)
    .captionAsHtml() (7)
    .description("The description") (8)
    .withData(new MyData()) (9)
    .hidden() (10)
    .disabled() (11)
    .responsive() (12)
    .withAttachListener(event -> { (13)
      // ...
    }).withDetachListener(event -> { (14)
      // ...
    }).withContextClickListener(event -> { (15)
      event.isDoubleClick();
      // ...
    }).errorHandler(event -> { (16)

    })
    // Label specific configuration
    .content("Label content") (17)
    .html() (18)
    .build();
1 Obtain a com.vaadin.ui.Label component builder
2 Set the component width to 100%
3 Set the component height to 50px
4 Add two css style class names
5 Set the component icon
6 Set the caption
7 Set the caption to be rendered as HTML
8 Set the description (tooltip)
9 Set a generic data value bound to the component
10 Set the component as hidden (not visible)
11 Set the component as disabled
12 Set the component as responsive
13 Register a AttachListener
14 Register a DetachListener
15 Register a ContextClickListener
16 Set the component ErrorHandler
17 Set the Label content
18 Set the Label content mode as HTML

For component container component types (for example, Layout components), the builder APIs provides a set of methods to add components to the container to build, also providing layout configuration attributes such as alignment and expand ratio, when supported.

VerticalLayout builder example
VerticalLayout verticalLayout = Components.vl() (1)
    .margin() (2)
    .spacing() (3)
    .add(COMPONENT_1).add(COMPONENT_2, COMPONENT_3) (4)
    .addAndAlign(COMPONENT_4, Alignment.TOP_CENTER) (5)
    .addAndExpand(COMPONENT_5, 0.5f) (6)
    .addAndExpandFull(COMPONENT_6) (7)
    .addAlignAndExpand(COMPONENT_7, Alignment.MIDDLE_CENTER, 1f) (8)
    .deferLocalization().build();
1 Obtain a com.vaadin.ui.VerticalLayout component builder
2 Enable layout margins
3 Enable layout spacing
4 Add components to the layout in given order
5 Add a component and set the component alignment
6 Add a component and set the component expand ratio
7 Add a component and set the component expand ratio to 1 (full)
8 Add a component and set the component alignment and expand ratio

7.1. Component configurators

When a component instance is already available, the Components API makes available a set of component configurator APIs, which can be used to configure a component instance, using the same API as the component builders.

The configure(AbstractComponent component) method can be used to obtain a component configurator for a generic com.vaadin.ui.AbstractComponent, to setup common component configuration attributes.

Other component confgurator APIs are provided to configure specific components, such a Label and Button, and the standard Vaadin layout components.

Label label = new Label();

Components.configure(label).fullWidth().content("my content").contentMode(ContentMode.PREFORMATTED); (1)

VerticalLayout verticalLayout = new VerticalLayout();

Components.configure(verticalLayout).spacing().addAndAlign(COMPONENT_1, Alignment.TOP_CENTER) (2)
    .addAndExpandFull(COMPONENT_2);
1 Label component configurator
2 VerticalLayout component configurator

7.2. Messages localization support

The component builder methods that can be used to configure a text to display for a component supports the message localization using the standard Holon Platform internationalization APIs.

The message localization attributes can be provided either using:

  • A Localizable object instance;

  • Or directly providing the message localization attributes, such as the message code, the default message and the optional message localization arguments.

A set of localizable message builder methods are available, for example, for standard Vaadin component attributes, such as the component caption and description.

Button btn = Components.button().caption("DefaultCaption", "button.caption.code") (1)
    .build();

Label lbl = Components.label()
    .content(Localizable.builder().message("DefaultMessage").messageCode("my.message.code").build()) (2)
    .build();
1 Set the Button caption using a default caption message (DefaultCaption) and a message localization code (button.caption.code)
2 Set a localizable Label content using a Localizable
In order for the message localization to work, a valid LocalizationContext instance must be available as a context resource using the default LocalizationContext resource key (i.e. the LocalizationContext class name). See the LocalizationContext documentation for details about the LocalizationContext API and the Context resources documentation for information about the Holon Platform Context architecture.
This module makes available a Vaadin session scope to use the Vaadin session as context resources scope.

7.2.1. Defer localization

By default, the component messages localization is performed immediately during the component building, when a localizable message is provided at component configuration time.

Since the Holon Platform localization APIs require that a LocalizationContext resource is available to perform messages localization, when this is not possible the messages localization will not work at component configuration time. The LocalizationContext resource must be also localized, i.e. the current Locale is configured and available, in order to ensure messages localization support.

When the LocalizationContext resource configuration or localization availability is expected after the component build time, the component builder APIs provide a method to defer the component messages localization: deferLocalization().

This method instructs the builder to resolve any message localization (for example the component caption and description) only when the component is attached to a parent layout, and this means only when the parent layout too is attached to the application UI.

Label lbl = Components.label() //
    .caption("Default caption", "caption.message.code") //
    .deferLocalization() (1)
    .build();
1 Set to defer the component messages localization at UI display time

7.3. Drag and Drop support

The Vaadin module component builders supports component drag and drop configuration, allowing to declare a component as draggable and configure the drag mode and attributes and to declare a component as a drop target providing the target configuration and logic to react to a drop event.

The standard Vaadin DragSourceExtension and DropTargetExtension extensions are used to configure components drag and drop capabilities.

The dragSource builder method can be used to declare a component as draggable. The provided Consumer can be used to configure the DragSourceExtension for the component.

The dropTarget builder method can be used to declare a component as a drop target and the provided BiConsumer can be used to configure the DropTargetExtension for the component and to obtain the actual component instance when a drop event occurs.

Label lbl = Components.label().content("Draggable").dragSource(dragSource -> { (1)
  dragSource.setEffectAllowed(EffectAllowed.MOVE); (2)
  dragSource.setDataTransferText("hello receiver");
  dragSource.addDragStartListener(e -> Notification.show("Drag event started"));
}).build();

VerticalLayout droppableLayout = Components.vl().fullWidth().caption("Drop things inside me")
    .dropTarget((dropTarget, component) -> { (3)
      dropTarget.setDropEffect(DropEffect.MOVE); (4)
      dropTarget.addDropListener(dropEvent -> { (5)
        dropEvent.getDragSourceComponent().ifPresent(dragged -> component.addComponent(dragged));
        Notification.show("DropEvent with data transfer text: " + dropEvent.getDataTransferText());
      });
    }).build();
1 Configure the Label as draggable
2 The DragSourceExtension can be configured using the provided Consumer function
3 Configure the VerticalLayout as a drop target
4 The DropTargetExtension can be configured using the provided BiConsumer function
5 The second argument of the BiConsumer function is the actual VerticalLayout instance, used in this case to add the dropped component to the layout when a drop event occurs

8. UI Components

The Vaadin module provides a set of additional UI components which can be used besides the standard Vaadin components.

8.1. Input

The Input interface represents a component that provides a user-editable value, which can be read and setted using the methods provided by the The ValueHolder API.

The Input API is parametrized on the value type it manages and the value management is provided by the ValueHolder API, which provides methods to:

  • Set the Input value.

  • Read the input value.

  • Provide and empty value representation.

  • Check whether a value is available.

  • Listen to value changes registering one or more ValueChangeListener.

Furthermore, the Input API supports two component state configuration options:

  • Read-only mode: When an Input is in read-only mode, the user can’t change the Input value.

  • Required mode: When an Input is in required mode, a required indicator symbol is visible.

The Input API is a general and abstract representation of an UI object which holds a value, the actual Vaadin Component representation can be obtained though the getComponent() method and used with the standard Vaadin APIs.

Input<String> stringInput = Components.input.string().build(); (1)

Component component = stringInput.getComponent(); (2)
VerticalLayout vl = Components.vl().add(component).build(); (3)
1 Create a String type input
2 Get the actual Input Component
3 Add the Input Component to a vertical layout

8.1.1. Create an Input component

An Input component can be created using the Components API, through the input sub-interface methods.

A typed Input builder method is available for each available Input value type. Besides the base Input types, as set of methods are available to create single and multi select Input components. See Selectable Input for details.

Input<String> stringInput = Components.input.string().build(); (1)
stringInput = Components.input.string(true).build(); (2)
Input<Integer> integerInput = Components.input.number(Integer.class).build(); (3)
Input<Boolean> booleanInput = Components.input.boolean_().build(); (4)
Input<Date> dateInput = Components.input.date(Resolution.DAY).build(); (5)
Input<LocalDate> localDateInput = Components.input.localDate().build(); (6)
Input<LocalDateTime> localDateTimeInput = Components.input.localDateTime().build(); (7)

SingleSelect<MyEnum> enumSingleSelect = Components.input.enumSingle(MyEnum.class).build(); (8)
MultiSelect<MyEnum> enumMultiSelect = Components.input.enumMulti(MyEnum.class).build(); (9)
1 String type Input
2 String type Input represented as a text area
3 Numeric (Integer in this case) type Input
4 Boolean type Input
5 Date type Input with resolution configuration
6 LocalDate type Input
7 LocalDateTime type Input
8 Enum type Input represented with a single select
9 Enum type Input represented with a multi select

Input component configuration:

The Input builder API provides methods both for the Input attributes al listeners configuration and for the associated Input Component configuration, including API methods for specific Input and Component types.

Input<String> stringInput = Components.input.string() //
    // Component configuration
    .fullWidth().styleName("my-style").caption("The caption") (1)
    // Input configuration
    .readOnly() (2)
    .tabIndex(10) (3)
    .withValue("Initial value") (4)
    .withValueChangeListener(event -> { (5)
      event.getValue();
      event.getOldValue();
      // ...
    }).withFocusListener(event -> { (6)
      // focused
    })
    // Specific String Input configuration
    .inputPrompt("The prompt") (7)
    .maxLength(100) (8)
    .blankValuesAsNull(true) (9)
    .textChangeEventMode(ValueChangeMode.BLUR) (10)
    .build();
1 Input Component configur
2 Set the Input as read-only
3 Set the tabulator index
4 Set the Input initial value
5 Register a ValueChangeListener
6 Register a FocusListener
7 Set the Input prompt
8 Set the String Input max length (the maximum number of cahracters)
9 Set to treat blank String values (with 0 length or whitespaces only) as null values.
10 Set the mode how the Input triggers vaue change events

See the next sections for details about the Input configuration attributes and features.

8.1.2. Obtain an Input component from a HasValue Component

An Input component can be obtained from a standard Vaadin HasValue Component using the from static method of the Input API.

TextField field = new TextField();

Input<String> stringInput = Input.from(field); (1)
1 Create a String type Input using a Vaadin TextField as concrete implementation

A com.vaadin.data.Converter can also be provided to obtain an Input component with a different value type from the original field type, providing the presentation to model type value conversion logic and vice-versa.

TextField field = new TextField();

Input<Integer> integerInput = Input.from(field, new StringToIntegerConverter("conversion error")); (1)
1 Create a Integer type Input using a Vaadin TextField as concrete implementation and providing a Converter to convert String type presentation values into Integer type model values and vice-versa

8.1.3. Obtain a HasValue Component from Input builders

The Input builder APIs can provide the Input instance also a standard Vaadin HasValue Component, which is represented by the convenience Field interface.

To obtain a Field type Input component, the Input builder API asField() method can be used.

Field<LocalDate> field = Components.input.localDate() (1)
    // configuration omitted
    .asField(); (2)

HasValue<LocalDate> hasValue = field; (3)
Component component = field;
1 Obtain a LocalDate type Input builder
2 Get the Input component as a LocalDate type Field
3 The obtained Field is a HasValue<LocalDate> and a Component

8.1.4. Input value conversion

The Input interface makes available a set of methods to create an Input from another Input or from a standard Vaadin HasValue Component with a different value type, providing a suitable converter to perform value conversions from the presentation value type to the model value type and vice-versa.

The stardard Vaadin com.vaadin.data.Conveter API is supported to provide the value conversion logic.

Input<String> stringInput = Components.input.string().build();

Input<Integer> integerInput = Input.from(stringInput, new StringToIntegerConverter("Conversion error")); (1)

Input<Boolean> booleanInput = Input.from(integerInput, (2)
    Converter.from(value -> Result.ok((value == null) ? Boolean.FALSE : (value.intValue() > 0)),
        value -> (value == null) ? null : (value ? 1 : 0)));

Input<Long> longInput = Input.from(new TextField(), new StringToLongConverter("Conversion error")); (3)
1 Convert a String presentation type Input into a Integer model type Input using the standard Vaadin StringToIntegerConverter
2 Obtain a Boolean type Input from the Integer type one providing the value conversion logic
3 Obtain a Long type Input from a String type TextField using the StringToLongConverter converter

Besides the standard Vaadin com.vaadin.data.Conveter, the Holon PropertyValueConverter can be used, providing the Property to which the converter is bound.

See the Property value conversion documentation for information about the PropertyValueConverter API.
Input<Integer> integerInput = Components.input.number(Integer.class).build();

final Property<Boolean> BOOL_PROPERTY = PathProperty.create("bool", Boolean.class); (1)

Input<Boolean> booleanInput = Input.from(integerInput, BOOL_PROPERTY,
    PropertyValueConverter.numericBoolean(Integer.class)); (2)
1 The Boolean type Property to use for the PropertyValueConverter
2 Create a Boolean type Input from an Integer type Input, using a the default numericBoolean PropertyValueConverter

8.1.5. Listening to Input value changes

The Input API supports ValueChangeListener registration to listen to Input value changes.

The value change event provide the current (changed) Input value, the previous (old) Input value, the value change source (i.e. the reference to the Input component which triggered the value change listeners) and whether the value change was originated by a user action or from server side code.

Input<String> stringInput = Components.input.string() //
    .withValueChangeListener(event -> { (1)
      String currentValue = event.getValue(); (2)
      String previousValue = event.getOldValue(); (3)
      boolean byUser = event.isUserOriginated(); (4)
      ValueHolder<String> source = event.getSource(); (5)
      // ...
    }).build();
1 Register a ValueChangeListener for the String type Input
2 Get the new value that triggered the value change event
3 Get the value of the Input before the value change event occurred
4 Get whether the event was triggered by user interaction, on the client side, or programmatically, on the server side
5 Get the source of the value change event, i.e. the Input component itself

Value change mode

The mode and the frequency with which the value change events are triggered can be customized for the Input components which supports it.

The MaySupportValueChangeMode API, extended by the Input API, allows to check if the value change mode configuration is supported by a specific Input component through the isValueChangeModeSupported() method.

If so, the value change mode can be configured specifying:

  • The com.vaadin.shared.ui.ValueChangeMode enumeration value which represents when and how often Input value changes are transmitted from the client to the server.

  • Set the value change timeout, i.e. how often value change events are triggered, when the ValueChangeMode is either LAZY or TIMEOUT.

Input<String> stringInput = Components.input.string().withValueChangeListener(event -> {
  // ...
}).valueChangeMode(ValueChangeMode.LAZY) (1)
    .valueChangeTimeout(1000) (2)
    .build();

boolean supported = stringInput.isValueChangeModeSupported(); (3)
if (supported) {
  stringInput.setValueChangeMode(ValueChangeMode.BLUR); (4)
}
1 Configure the ValueChangeMode using the Input builder. The builder methods to configure the value change mode are available only for Input components which support it.
2 Configure the value change timeout using the Input builder
3 Check if the value change mode configuration is supported by the Input component
4 Set the value change mode to BLUR using the Input API

8.2. ValidatableInput

The ValidatableInput interface represents an Input component which supports value validation using the standard Holon Platform Validator API.

A ValidatableInput can be obtained in the following ways:

1. From an existing Input or HasValue component: Using the from static method of the ValidatableInput API.

Input<String> stringInput = Components.input.string().build();

ValidatableInput<String> validatableInput = ValidatableInput.from(stringInput); (1)

validatableInput = ValidatableInput.from(new TextField()); (2)
1 Create a ValidatableInput from a String Input
2 Create a ValidatableInput from a TextField

2. Using the Input builder: Using the validatable() builder method, which returns a ValidatableInputBuilder API that can be used to configure the ValidatableInput, for example adding one or more Validator, before returning it.

validatableInput = Components.input.string() //
    .caption("The caption") //
    .validatable() (1)
    .required("Value is required") (2)
    .withValidator(Validator.max(100)) (3)
    .build();
1 Get the builder API to obtain a ValidatableInput
2 Add a default required (the input is not empty) validator using the provided error message
3 Add a `Validator

8.2.1. Registering Validators and perform validation

The addValidator method of the ValidatableInput API can be used to register value validators for the Input component.

The Validator API can be used to obtain a set of builtin validators, allowing to specify the validation error message, even using a localizable message code. See the Holon Platform Validator API for details.

ValidatableInput<String> validatableInput = Components.input.string().validatable().build(); (1)

validatableInput.addValidator(Validator.max(100)); (2)
validatableInput.addValidator(Validator.email("Must be a valid e-mail address", "invalid.email.message.code")); (3)
validatableInput
    .addValidator(Validator.create(value -> value.length() >= 3, "Must be at least 3 characters long")); (4)
1 Create a String type ValidatableInput
2 Add a builtin Validator to check the Input value is maximum 100 characters long, using the default validation error message
3 Add a builtin Validator to check the Input value is a valid e-mail address, providing a localizable validation error message
4 Add a Validator to check the Input value is at least 3 characters long and provide the validation error message

The actual value validation can be performed using the Validatable API, which provides the following methods:

  • validate(): Validate the current Input value using the registered Validator s. Throws a ValidationException if the value is not valid, which provides the validation error message/s. The ValidationException is Localizable, providing optional message code and arguments for validation message localization.

  • isValid(): Returns whether the current Input value is valid, swallowing any ValidationException.

try {
  validatableInput.validate(); (1)
} catch (ValidationException e) {
  // do something at validation failure
}

boolean valid = validatableInput.isValid(); (2)
1 Validate the Input value. The ValidationException is thrown if the validation fails.
2 Checks whether the current Input value is valid, swallowing any ValidationException.

8.2.2. Automatic validation at Input value change

Value validation can be automatically triggered each time the Input value changes.

To enable this behaviour, either the setValidateOnValueChange method of the ValidatableInput API or the validateOnValueChange method of the ValidatableInput builder API can be used.

ValidatableInput<String> validatableInput = Components.input.string().validatable() //
    .validateOnValueChange(true) (1)
    .build();

validatableInput.setValidateOnValueChange(false); (2)
1 Use the ValidatableInput builder API to enable the automatic value validation at Input value change
2 Use the ValidatableInput API to disable the the automatic value validation at Input value change

8.2.3. Validation errors notification

By default, the standard Vaadin component error notification is used to notify validation errors on ValidatableInput components. As per default behaviour, an invalid component is decorated with a proper CSS style class (for example to show a red border around the component itself) and the validation error is notified using a tooltip.

To change this behavior and to control how the validation errors are notified to the user, the ValidationStatusHandler API can be used.

A ValidationStatusHandler listens to validation status change events and the provided validation event can be used to:

  • Obtain the current component validation status: UNRESOLVED (no value validation has been made yet), VALID (validation passed), INVALID (validation failed).

  • Obtain the current validation error messages if the component is in the INVALID status. The error messages can be obtained as Localizable to support message localization.

  • Get the source component against whom the validation was performed.

The ValidationStatusHandler can be configured either using the setValidationStatusHandler method of the ValidatableInput API or the validationStatusHandler method of the ValidatableInput builder API.

ValidatableInput<String> validatableInput = Components.input.string().validatable() //
    .validationStatusHandler(event -> { (1)
      Status status = event.getStatus();
      // ....
    }).build();

validatableInput.setValidationStatusHandler(event -> { (2)
  event.getError();
  // ...
});
1 Use the ValidatableInput builder API to configure a ValidationStatusHandler
2 Use the ValidatableInput API to configure a ValidationStatusHandler

The ValidationStatusHandler also provides static methods to obtain a default validation status handler either using a Label component or a Vaadin Notification component.

Label statusLabel = new Label();
ValidationStatusHandler vsh = ValidationStatusHandler.label(statusLabel); (1)

ValidationStatusHandler usingNotifications = ValidationStatusHandler.notification(); (2)
1 Create a ValidationStatusHandler using a Label to display validation errors.
2 Create a ValidationStatusHandler using a Notification to notify validation errors.

8.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 API, which provides methods to check if some item is selected, obtain the selected item/s and change the current selection.

A selectable Input can be obtained using the Components API, through the singleSelect and multiSelect methods and their specializations.

Furthermore, a SelectionListener can be registered to listen to selection changes.

SingleSelect example
SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class) (1)
    .caption("Single select").build();

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) (1)
    .caption("Multi select").build();

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

8.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.CheckBoxGroup

com.vaadin.ui.RadioButtonGroup

SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class, RenderingMode.OPTIONS) (1)
    .build();

MultiSelect<TestData> multiSelect = Components.input.multiSelect(TestData.class, RenderingMode.SELECT) (2)
    .build();
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

8.3.2. Using selectable Inputs for Enumerations

The Components API provides convenience methods to build a selectable Input using the values of an enumeration as selection items.

The enumeration selectable Input can be obtained either as a single select or a multiple select, even specifying the RenderingMode to use.

SingleSelect<MyEnum> singleSelect = Components.input.enumSingle(MyEnum.class).build(); (1)
singleSelect = Components.input.enumSingle(MyEnum.class, RenderingMode.NATIVE_SELECT).build(); (2)

MultiSelect<MyEnum> multiSelect = Components.input.enumMulti(MyEnum.class).build(); (3)
1 Create a SingleSelect Input for the MyEnum enumeration
2 Create a SingleSelect Input for the MyEnum enumeration using NATIVE_SELECT as rendering mode
3 Create a MultiSelect Input for the MyEnum enumeration

8.3.3. 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.

  • Using a standard Vaadin DataProvider.

SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class)
    .items(new TestData(1), new TestData(2)) (1)
    .build();

singleSelect = Components.input.singleSelect(TestData.class)
    .dataSource(ItemDataProvider.create(q -> 2, (q, o, l) -> Stream.of(new TestData(1), new TestData(2)))) (2)
    .build();

singleSelect = Components.input.singleSelect(TestData.class)
    .dataSource(DataProvider.ofItems(new TestData(1), new TestData(2))) (3)
    .build();
1 Create a select input using an explicitly provided items set
2 Create a select input using a Holon platform ItemDataProvider
3 Create a select input using a Vaadin DataProvider

8.3.4. Item captions and icons

The selectable input builders allow to explicitly set the selection item captions and icons. For the items caption configuration, the Holon Platform localization architecture is supported, and the builders provide methods to specify the captions using a Localizable object or a message code.

See Messages localization support for details about messages localization.

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, VaadinIcons.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 ? VaadinIcons.STAR : VaadinIcons.STAR_O) (2)
    .build();
1 Set an item caption generator which provides the item description as item caption
2 Set an item icon generator

8.3.5. SingleSelect caption filter

For the SingleSelect type Inputs which use the SELECT rendering mode, the UI component allows the user to type a filtering text to search for a selection item, and such filter by default is referred to item captions.

When using a fixed item set, the in-memory caption filter can be customized using the filteringMode(…​) builder method.

SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class)
    .items(new TestData(1), new TestData(2)) // set the items
    .filteringMode(
        (itemCaption, filterText) -> itemCaption.toLowerCase().startsWith(filterText.toLowerCase())) (1)
    .build();

When using a ItemDataProvider or a Vaadin DataProvider, an overloaded form of the dataSource(…​) method can be used, which accept as second parameter a function to provide a suitable query filter using the current user filtering text.

final TestData ONE = new TestData(1);
final TestData TWO = new TestData(2);

SingleSelect<TestData> singleSelect = Components.input.singleSelect(TestData.class)
    .dataSource(ItemDataProvider.create(q -> 2, (q, o, l) -> Stream.of(ONE, TWO)), filterText -> QueryFilter
        .startsWith(ConstantConverterExpression.create("description"), filterText, true))
    .build();

8.3.6. Using the Property model with selectable Inputs

The Holon platform Property model is fully supported by the selectable Input builders, which allows 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.

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 query 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), // Datastore
        () -> ID.gt(0L)) (1)
    .itemCaptionGenerator(propertyBox -> propertyBox.getValue(DESCRIPTION)).build();
1 Set a QueryConfigurationProvider which provides a QueryFilter to select only the items with the ID property that is greater than 0
Selection model converter

When the Holon platform Property model is used for selectable Inputs and the selection value type is not a PropertyBox, i.e. the selection items are not of the same type of the Input value type, a conversion function is required to obtain the PropertyBox item from a selection value when the value is setted in the Input component, either using setValue(…​) or select(…​).

For example, if a String type Property is declared as selection property, the Input value will be the the value of the selection property (of String type), while the selection items are PropertyBox.

When a Datastore is provided as the data source of the select, a default item converter is configured. The default item converter tries to obtain the PropertyBox item by performing a query on the Datastore and using a filter predicate to get the item for which the selection property value is equal to the value to select, assuming that the selection property acts as primary key.

To change the default conversion strategy, a custom item converter can be provided to the select input builder using the itemConverter(…​) method:

Datastore datastore = obtainDatastore();

final PathProperty<Long> ID = PathProperty.create("id", Long.class);
final PathProperty<String> DESCRIPTION = PathProperty.create("description", String.class);
final DataTarget<?> TARGET = DataTarget.named("testData");
final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);

SingleSelect<Long> singleSelect = Components.input.singleSelect(ID)
    .dataSource(datastore, DataTarget.named("testData"), PROPERTIES)
    .itemConverter(
        value -> datastore.query().target(TARGET).filter(ID.eq(value)).findOne(PROPERTIES).orElse(null)) (1)
    .build();
1 Set an ItemConverter providing the query to perform to obtain the item PropertyBox from the selection ID property value

8.4. Input groups

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.

8.4.1. Property Inputs rendering

By default, the Holon platform Property rendering architecture is used to automatically generate a suitable Input component for each Property of Input group property set, according to the Property type.

The Input type is used as target rendering type to generate the Input components.

See Property renderers and presenters for further information about Property renderers.
final NumericProperty<Long> ID = NumericProperty.longType("id");
final StringProperty DESCRIPTION = StringProperty.create("description");

final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) (1)
    .build();
1 Configure the input group property set. The property Input fields are automatically generated using the available Property Rendereres from the default registry

8.4.2. Explicit Input and HasValue binding

To explicitly set the Input component to use for a Property of the input group property set, the bind(…​) builder methods of the PropertyInputGroup builder API can be used.

The bind(…​) builder method can therefore be used to override the default property Input generation only when a custom Input type is required.

The standard HasValue Components are also supported to define a Property binding.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES)
    .bind(ID, Components.input.number(Long.class).build()) (1)
    .bind(DESCRIPTION, new TextField()) (2)
    .build();
1 Bind the ID property to a Long type Input component
2 Bind the DESCRIPTION property to a TextField

8.4.3. Input value convertes

When an Input component or HasValue component must be bind to a Property with a different type, a value converter can be provided to convert the Input value to the required Property type and vice-versa.

Specialized bind methods of the PropertyInputGroup builder API are available to use a standard Vaadin com.vaadin.data.Converter to provide the conversion logic.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES)
    .bind(ID, Components.input.string().build(), new StringToLongConverter("Conversion error")) (1)
    .bind(ID, new TextField(), new StringToLongConverter("Conversion error")) (2)
    .build();
1 Bind the ID property to a String type Input component and use a StringToLongConverter to convert the String values to the required Long property type
2 Bind the ID property to a TextField and use a StringToLongConverter to convert the String values to the required Long property type

8.4.4. Manage the property values using a PropertyBox

The property values are setted and obtained using the PropertyBox data container, using 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.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES).build();

group.setValue(PropertyBox.builder(PROPERTIES).set(ID, 1L).set(DESCRIPTION, "TestDescription").build()); (1)

PropertyBox value = group.getValue(); (2)

group.addValueChangeListener(e -> { (3)
  PropertyBox changedValue = e.getValue();
});
1 Set the input group property values using a PropertyBox with a matching property set
2 Get the input group property values as a PropertyBox instance
3 Add an input group value change listener

8.4.5. 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.

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

8.4.6. 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.

Inputs bound to hidden properties are never returned from the PropertyInputGroup methods which allow to inspect the available binding, such as getInputs(), getInput(Property<T> property) or stream().

The hidden(Property property) method of the group builder can be used to set a property as hidden.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .hidden(ID) (1)
    .build();
1 The ID property is setted as hidden and will not be displayed nor managed by the input group

8.4.7. 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.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .defaultValue(DESCRIPTION, property -> "Default") (1)
    .build();
1 Use a DefaultValueProvider to provide the DESCRIPTION property default value

8.5. Input group validation

A PropertyInputGroup support both property values and group value validation, supporting both the Holon Platform Validator API and the standard Vaadin validators.

When a validator in bound to the overall Input group, the value to validate will be represented by the PropertyBox type, which contains all the current property values.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .withValidator(DESCRIPTION, Validator.max(100)) (1)
    .withValidator(ID, com.vaadin.data.Validator.from(id -> id != null, "Id must be not null")) (2)
    // group validation
    .withValidator(Validator.create(propertyBox -> propertyBox.getValue(ID) > 0,
        "The ID value must be greater than 0")) (3)
    .build();
1 Add a Validator bound to the DESCRIPTION property to check that the value size is not greater than 100 characters
2 Add a com.vaadin.data.Validator to check the ID property value is not null
3 Add an overall (group) Validator which reads the ID property value from the group PropertyBox value and checks it is greater than 0

Furthermore, 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 along with the property Input component.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .required(ID) (1)
    .required(ID, "The ID value is required") (2)
    .build();
1 Add a required validator to the ID property
2 Add a required validator to the ID property providing a custom validation error message

The input group validation can be requested using the validate() and it is performed by invoking:

  1. All the available Property validators for each property of the input group property set;

  2. Then the overall group validators, if any.

Besides the validate() method, the input group validation is performed by default when the Input group PropertyBox value is requested using the getValue() method. To skip the group validation when requesting the Input group value, a getValue(boolean validate) method is available and false can be specified for the validate parameter.

A further getValueIfValid() method can be used to obtain the Input group value only when it is valid, after performing the validations. If validation fails, an empty Optional is returned.

PropertyInputGroup group = createInputGroup();

try {

  group.validate(); (1)

  PropertyBox value = group.getValue(); (2)

} catch (ValidationException e) {
  // validation failed
}

PropertyBox value = group.getValue(false); (3)

group.getValueIfValid().ifPresent(propertyBox -> { (4)
  // ...
});
1 Explicit group validation: if some kind of 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

8.5.1. Property validators

Since the Property API supports direct validators registration for each Property instance, the property validators are inherited by default as Input group validators for each Property of the Input group property set.

This behavior can be disabled using the ignorePropertyValidation() method of the PropertyInputGroup builder API.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .ignorePropertyValidation() (1)
    .build();
1 Set to ignore any Property Validator when binding the property to an Input component, i.e. to not inherit property validators when the property-Input binding is performed.

8.5.2. Validation configuration

The Input group provides some configuration options to tune the validation strategy. The validation configuration can be controlled through the PropertyInputGroup builder API in order to:

  • Enable or disable the automatic Input validation when the Input value changes (this behavior is enabled by default).

  • Whether to stop property validation at first validation failure. When active, only the first ValidationException is thrown. Otherwise, a ValidationException which groups all the occurred validation exceptions is thrown, providing all the validation error messages (the default behavior).

  • Whether to stop overall group validation at first validation failure. When active, only the first ValidationException is thrown. Otherwise, a ValidationException which groups all the occurred validation exceptions is thrown, providing all the validation error messages (the default behavior).

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .validateOnValueChange(false) (1)
    .stopValidationAtFirstFailure(true) (2)
    .stopOverallValidationAtFirstFailure(true) (3)
    .build();
1 Disable the automatic validation at Input value change
2 Stop property validation at first validation failure
3 Stop overall group validation at first validation failure

8.5.3. Validation errors notification

A ValidationStatusHandler can be used to control how the validation errors are notified to the user, listening to validation status change events of the PropertyInputGroup.

See Validation errors notification for details about the ValidationStatusHandler API.

The property input group supports ValidationStatusHandler configuration both for property and overall validation.

Label statusLabel = new Label();

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .validationStatusHandler(validationEvent -> { (1)
      // ...
    }).propertiesValidationStatusHandler(validationEvent -> { (2)
      // ...
    }).validationStatusHandler(statusLabel) (3)
    .build();
1 Configure a ValidationStatusHandler for the overall (group) validation
2 Configure a ValidationStatusHandler for the properties validation
3 Convenience method to use a Label as validation status handler

8.6. Handling value changes

The PropertyInputGroup builder API provides methods to register one or more ValueChangeListener in order to:

  • Listen to Input value changes for the Input components bound to group property set.

  • Listen to the Input group value changes, where the value type is represented by the PropertyBox type.

Furthermore, the PropertyInputGroup builder API makes available a set of methods to configure the value change notification strategy, i.e. the mode and the frequency with which the value change events are triggered, both for property level and group level value change events.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .withValueChangeListener(e -> { (1)
      PropertyBox value = e.getValue();
      // ...
    }).withValueChangeListener(DESCRIPTION, e -> { (2)
      String description = e.getValue();
      // ...
    }) //
    .valueChangeMode(ValueChangeMode.BLUR) (3)
    .valueChangeMode(DESCRIPTION, ValueChangeMode.LAZY) (4)
    .valueChangeTimeout(DESCRIPTION, 500) (5)
    .build();
1 Add an overall value change listener
2 Add a value change listener to the Input bound to the DESCRIPTION property
3 Set the default ValueChangeMode for all inputs
4 Set the ValueChangeMode only for the Input bound to the DESCRIPTION property
5 Set the value change timeout to 500 ms only for the Input bound to the DESCRIPTION property

In addition to the standard ValueChangeListener, the PropertyInputValueChangeListener interface is also supported to listen to Property/Input pairs value changes.

The PropertyInputValueChangeListener provides the current PropertyInputBinder reference, besides of the standard ValueChangeEvent. The PropertyInputBinder makes available several methods to inspect the input group to which the value change source belongs, for example in order to obtain the values of other Input components.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .withValueChangeListener(DESCRIPTION, (event, binder) -> { (1)
      String description = event.getValue();
      Long id = binder.requireInput(ID).getValue();
      // ...
    }).build();
1 Add a PropertyInputValueChangeListener to the Input bound to the DESCRIPTION property, which provides the current PropertyInputBinder to inspect all available group Inputs

8.7. Dealing with Virtual properties

By default, the PropertyInputGroup renders as an Input all the properties of the property set that has been provided at build time, including any VirtualProperty.

Since a VirtualProperty is a read-only property, the Input bound to a VirtualProperty is configured in read-only mode by default.

If the VirtualProperty value is calculated using the values of other properties of the PropertyInputGroup property set, can be useful to refresh the displayed Input value bound to the virtual property when one or more property of set changes.

For this purpose, the refresh(…​) methods of the PropertyInputGroup can be used. This methods are available from the PropertyInputBinder super-interface. You can refresh a specific property or all the properties of the set, even only the read-only ones.

final VirtualProperty<String> VIRTUAL = VirtualProperty.create(String.class,
    propertyBox -> propertyBox.getValue(ID) + " -" + propertyBox.getValue(DESCRIPTION));

final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION, VIRTUAL);

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .withValueChangeListener(DESCRIPTION, (event, binder) -> { (1)
      binder.refresh(VIRTUAL);
    }) //
    .build();
1 Add a PropertyInputValueChangeListener to the Input bound to the DESCRIPTION property, and refresh the VIRTUAL property Input value when the DESCRIPTION property value changes

To disable the read-only properties rendering and management, the excludeReadOnlyProperties() builder method can be used.

PropertyInputGroup group = Components.input.propertyGroup().properties(PROPERTIES) //
    .excludeReadOnlyProperties() (1)
    .build();
1 Exclude any read-only property from Input rendering and binding

8.8. Input Forms

The PropertyInputForm component represents a PropertyInputGroup which is also a UI Component, and thus can be directly used to display and manage the Input components in the UI.

It extends the ComposableComponent API, which represents an UI component with a base layout and the capability to compose a set of UI components on this layout.

The Composer API is used to compose the Input components on the UI, and must be provided to the PropertyInputForm at configuration time, along with the base layout component on which the Input components will be organized.

When a PropertyInputForm component is obtained using the Components API, a set of methods are available to create a PropertyInputForm using one of the standard Vaadin layouts. Using these methods, a default ComponentContainer composer is provided, which adds the Input components to the base layout in the order they are provided by the PropertyInputForm property set.

The ComposableComponent interface provides the componentContainerComposer() static method to obtain a default composer which uses a Vaadin ComponentContainer as base layout.
PropertyInputForm form = Components.input.form().properties(PROPERTIES).build(); (1)
form = Components.input.formVertical().properties(PROPERTIES).build(); (2)
form = Components.input.formHorizontal().properties(PROPERTIES).build(); (3)
form = Components.input.formGrid().properties(PROPERTIES).build(); (4)
1 Create a PropertyInputForm using a FormLayout as base content layout
2 Create a PropertyInputForm using a VerticalLayout as base content layout
3 Create a PropertyInputForm using a HorizontalLayout as base content layout
4 Create a PropertyInputForm using a GridLayout as base content layout

8.8.1. Base layout initialization

A initializer(…​) builder method is available to perform custom configuration of the base layout component of the PropertyInputForm.

PropertyInputForm form = Components.input.formGrid().properties(PROPERTIES) //
    .initializer(gridLayout -> { (1)
      gridLayout.setSpacing(true);
      gridLayout.addStyleName("my-style");
    }).build();
1 Provide base layout component initialization using an initializer

8.8.2. Input components composition

The Composer API is used to compose the Input components on the PropertyInputForm base layout, and can be provided at configuration time either to provide a custom component composition strategy.

The Input components composition using the currently configured Composer is triggered by the compose() method. By default, the components composition is automatically performed when the PropertyInputForm component is a attached to a parent layout.

PropertyInputForm form = Components.input.form(new FormLayout()) (1)
    .properties(PROPERTIES).composer((layout, source) -> { (2)
      source.getValueComponents().forEach(c -> layout.addComponent(c.getComponent()));
    }).build();
1 A FormLayout instance is provided as base layout component
2 Configure a Composer to compose the Input components on the form base layout. The PropertyComponentSource type argument can be used to obtain the available Input components

To disable this behaviour, the composeOnAttach(…​) method of the PropertyInputForm builder API can be used.

PropertyInputForm form = Components.input.form(new FormLayout()).properties(PROPERTIES) //
    .composer((layout, source) -> {
      source.getValueComponents().forEach(c -> layout.addComponent(c.getComponent()));
    }).composeOnAttach(false) (1)
    .build();

form.compose(); (2)
1 Disable the automatic Input components composition when the PropertyInputForm component is a attached to a parent layout
2 Explicitly use the compose() method to perform components composition

8.8.3. Input components width mode

When the Input components are composed on a PropertyInputForm content component, the ComponentsWidthMode enumeration can be used to configure the strategy to use to handle the component widths.

The available strategies are:

  • NONE: The width of each Input component will not be altered.

  • AUTO: The width of each Input component will adjusted according to parent layout width: if the parent layout is 100% wide, the components width will be setted to 100%.

  • FULL: The width of each Input component will always be setted to 100%.

The default strategy is AUTO.

The strategy can be changed using the componentsWidthMode method of the PropertyInputForm builder API.

PropertyInputForm form = Components.input.form().properties(PROPERTIES)
    .componentsWidthMode(ComponentsWidthMode.FULL) (1)
    .build();
1 Set the ComponentsWidthMode to FULL: all the form Input components will be 100% wide

8.8.4. Input components configuration

The ComposableComponent type builders, including the PropertyInputForm builder API, allow to provide custom configurator functions in order to configure the Input components just after they are rendered.

For each Property of the PropertyInputForm property set, you can provide a Consumer to use as ComponentConfigurator, which makes available a complete set of methods to configure the Input Component associated to a Property before rendering it in UI. For example, you can set the component size, the icon, the description and so on.

PropertyInputForm form = Components.input.form().properties(PROPERTIES)
    .componentConfigurator(ID, cfg -> cfg.styleName("id-input").description("The ID")) (1)
    .componentConfigurator(DESCRIPTION, cfg -> cfg.icon(VaadinIcons.CLIPBOARD_TEXT)) (2)
    .build();
1 Configure the ID property Component
2 Configure the DESCRIPTION property Component

8.9. View components

The ViewComponent API represents a UI component which can be used to display a value on the user interface. Unlike an Input type component, the value cannot be changed by the use.

As a ValueHolder, the view component value can be setted and obtained using the setValue and getValue methods.

Just like an Input type component, the actual UI component to display is obtained form the getComponent() method.

The Components API can be used to obtain a default ViewComponent implementation (backed by a Vaadin Label component) for a specific value type, using the view.component(Class<? extends T> valueType) method.

ViewComponent<String> view = Components.view.component(String.class) (1)
    .caption("TheCaption", "caption.message.code") (2)
    .icon(VaadinIcons.CAMERA) //
    .styleName("my-style") //
    .build();

view.setValue("TestValue"); (3)

String value = view.getValue(); (4)
1 Get a ViewComponent builder using a String value type
2 Configure the ViewComponent component
3 Set the ViewComponent value
4 Get the ViewComponent value

8.9.1. View component Groups and Forms

Just like the Input components, a ViewComponent set can be organized in groups and handled using forms.

These component containers rely on the Holon Platform the Property model to bind each ViewComponent to a Property and they use the PropertyBox type to provide the property values to show.

final NumericProperty<Long> ID = NumericProperty.longType("id");
final StringProperty DESCRIPTION = StringProperty.create("description");

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.setValue(PropertyBox.builder(PROPERTIES).set(ID, 1L).set(DESCRIPTION, "Test").build()); (3)
PropertyBox value = viewForm.getValue(); (4)
1 Create a PropertyViewGroup using the PROPERTIES property set
2 Create a PropertyViewForm using the PROPERTIES property set and a VerticalLayout as base layout
3 Set the form value using a PropertyBox
4 Get the form value as a PropertyBox

The PropertyViewGroup and PropertyViewForm provide all the base components configuration capabilities of their corresponding PropertyInputGroup and PropertyInputForm implementations.

So, for example, it is possible to configure a Composer to customize the ViewComponent components composition strategy for a PropertyViewForm or to provide component initializers at configuration time.

PropertyViewForm viewForm = Components.view.form(new FormLayout()).properties(PROPERTIES) //
    .initializer(layout -> layout.setMargin(true)) (1)
    .composer((layout, source) -> { (2)
      source.getValueComponents().forEach(c -> layout.addComponent(c.getComponent()));
    }) //
    .componentConfigurator(DESCRIPTION, cfg -> cfg.styleName("my-style")) (3)
    .componentsWidthMode(ComponentsWidthMode.FULL) (4)
    .build();
1 Base layout initializer
2 Custom component Composer
3 DESCRIPTION property ViewComponent component configurator
4 Set the ComponentsWidthMode to FULL: all the form ViewComponent components will be 100% wide

8.9.2. ViewComponent captions

When the PropertyViewForm builder API makes available some additional methods to directly configure the captions of the ViewComponent managed by the form.

For each Property of the form property set, the ViewComponent caption can be setted using the propertyCaption method or hidden using the hidePropertyCaption method.

PropertyViewForm viewForm = Components.view.form(new FormLayout()).properties(PROPERTIES) //
    .propertyCaption(DESCRIPTION, "My caption") (1)
    .hidePropertyCaption(ID) (2)
    .build();
1 Set the caption for the ViewComponent bound to the DESCRIPTION property
2 Hide the caption for the ViewComponent bound to the ID property

9. Item listing

The ItemListing API can be used to display a set of items as tabular data using a Grid type UI component and to manage the data items.

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 API provides a set of methods to control the listing appearance, showing and hiding columns, sorting and refreshing the item set, and so on. See Item listing API and configuration.

Two ItemListing implementations are available to use this component:

  • PropertyListing: Uses a PropertyBox as item type and the listing column ids are the Property instances of the provided property set.

  • BeanListing: Uses a Java Bean as item type and the listing column ids are the Bean property names.

9.1. Item listing properties

An ItemListing component manages the its data as a set of items and each item attribute is identified by a property which also acts as listing column id.

The available item attributes represent the item property set and can be used to access and display the item data in the listing.

Each concrete ItemListing implementation is bound to a specific item type and property type.

Since each property id represents an item listing column id, it is used be the builder API to identify the listing columns and provide a set of column configuration methods. See Item listing API and configuration for details.

9.1.1. Visible properties

By default, all the properties of the listing property set are rendered as listing columns, in the same order they are provided by the property set itself.

The listing columns display can be changed as described below.

1. Explicit column selection and display order:

To explicitly select which properties are to be rendered as listing columns and to declare the columns order, the ItemListing builder API provides specialized versions of the build() method, which accept an array or an Iterable of property ids (whose type will be the concrete type used by the specific ItemListing implementation) to declare the visible columns and their order.

For example, when using the PropertyListing implementation, the item’s property ids will be represented using the Property abstraction and the visible item listing columns (and their order) can be configured as in the example below:

final NumericProperty<Long> ID = NumericProperty.longType("id");
final StringProperty DESCRIPTION = StringProperty.create("description");
final BooleanProperty ACTIVE = BooleanProperty.create("active");

final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION, ACTIVE);

PropertyListing listing = Components.listing.properties(PROPERTIES) (1)
    .build(DESCRIPTION, ID); (2)
1 The PROPERTIES property set is used as item property set. By default, the displayed column set and ordering would be: ID, DESCRIPTION, ACTIVE.
2 Change the listing visible columns and their order: the DESCRIPTION property first and then the ID property

2. Default column display ordering configuration:

The ItemListing builder API provides a set of methods to configure the default column display ordering, which is applied only when an explicit ordering is not provided using the specialized build(…​) methods as described in the previous section.

The available column position configuration methods are:

  • displayAsFirst(P property): Display the column represented by given property id before any other listing column.

  • displayAsLast(P property): Display the column represented by given property id after any other listing column.

  • displayBefore(P property, P beforeProperty): Display the column represented by given property id before the listing column represented by the beforeProperty property id.

  • displayAfter(P property, P afterProperty): Display the column represented by given property id after the listing column represented by the afterProperty property id.

The property id type (P) depends on the concrete ItemListing implementation.

final NumericProperty<Long> ID = NumericProperty.longType("id");
final StringProperty DESCRIPTION = StringProperty.create("description");
final BooleanProperty ACTIVE = BooleanProperty.create("active");

final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION, ACTIVE);

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .displayAsFirst(ACTIVE) (1)
    .displayBefore(ID, DESCRIPTION) (2)
    .displayAfter(DESCRIPTION, ACTIVE) (3)
    .build();
1 Configure the column which corresponds to the ACTIVE property id to be displayed as first
2 Configure the ID column to be displayed before the DESCRIPTION column
3 Configure the DESCRIPTION column to be displayed after the ACTIVE column

9.2. PropertyListing

A PropertyListing implementation can be obtained using the Components API, through the listing.properties method.

The Property set to use to configure the PropertyListing must be provided, for each Property of the property set a listing column and configured to the Property types. Each column will be identified by the Property itself.

In particular, for each Property of the property set:

  • 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. See Messages localization support for details.

  • 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 Component in the listing column cells.

final NumericProperty<Long> ID = NumericProperty.longType("id");
final StringProperty DESCRIPTION = StringProperty.create("description");

final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);

PropertyListing listing = Components.listing.properties(PROPERTIES).build(); (1)
1 Create a PropertyListing using given PROPERTIES property set
See the Item listing API and configuration to learn about the item listing API operations and configuration options.

9.2.1. Items type and identifiers

The PropertyBox type is used to represent a PropertyListing item, i.e. a listing row. For each property listing row, the PropertyBox type item collects the values of each Property bound to the listing columns.

By default, the listing property set identifier properties are used to provide the PropertyBox items identifiers, if available. See the property set identifier properties documentation for details.

final NumericProperty<Long> ID = NumericProperty.longType("id");
final StringProperty DESCRIPTION = StringProperty.create("description");

final PropertySet<?> PROPERTIES = PropertySet.builderOf(ID, DESCRIPTION).identifier(ID).build(); (1)

PropertyListing listing = Components.listing.properties(PROPERTIES).build();
1 Set the ID property as property set identifier property

9.2.2. Items data source

The PropertyBox type item set handled by the PropertyListing is provided using an items data source, which can be configured using the PropertyListing builder API using the dataSource methods.

The ItemDataProvider API is used to represent the items data source.

An ItemDataProvider implementation provides methods to obtain the item set size and to load a subset of the items using a limit which represents the subset size and a offset index to specify which subset to return. For a PropertyListing, the items subset is returned as a Stream of PropertyBox instances.

The ItemDataProvider methods accepts a QueryConfigurationProvider instance, to provide the QueryFilter and the QuerySort to be used to query the item set.

See the Query documentation for information about the query definition using the Holon Platform Property model.
ItemDataProvider<PropertyBox> dataProvider = ItemDataProvider.create(cfg -> 0, (1)
    (cfg, offset, limit) -> Stream.empty()); (2)

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .dataSource(dataProvider) (3)
    .build();
1 Create a ItemDataProvider which always returns a 0 size item set
2 Return an empty Stream as item set
3 Create a PropertyListing using the dataProvider as items data source

A convenience method is available to use the Datastore API as items data source, providing the DataTarget to use to perform the query on the concrete persistence source.

Datastore datastore = getDatastore();

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .dataSource(datastore, DataTarget.named("test")) (1)
    .build();
1 Use a Datastore as data source. The Datastore API will be used to perform the queries to obtain the items set, with the given DataTarget as query target

9.2.3. Item identifier configuration

When the property set of the PropertyListing, used to represent the PropertyBox type items, does not provide one or more identifier properties as described in the Items type and identifiers section, the ItemIdentifierProvider interface can be used to provide the item identifiers.

ItemDataProvider<PropertyBox> dataProvider = getDataProvider();

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .dataSource(dataProvider, item -> item.getValue(ID)) (1)
    .build();

listing = Components.listing.properties(PROPERTIES) //
    .dataSource(getDatastore(), DataTarget.named("test"), 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 identifier
2 A shorter builder method to use the ID property as item identifier when a Datastore is used as items data source

9.2.4. Item query configuration

One or more QueryConfigurationProvider can be registered in the PropertyListing to provide additional and dynamic items query configuration elements, such as QueryFilter and QuerySort.

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .dataSource(getDatastore(), DataTarget.named("test")) //
    .withQueryConfigurationProvider(new QueryConfigurationProvider() { (1)

      @Override
      public QueryFilter getQueryFilter() {
        return ID.gt(0L);
      }

      @Override
      public QuerySort getQuerySort() {
        return DESCRIPTION.asc();
      }

    }).build();
1 Add a QueryConfigurationProvider to the PropertyListing

9.2.5. Using VirtualProperty for "generated" columns

The VirtualProperty abstraction can be used to declare generated columns for a PropertyListing component, i.e. columns whose contents are not directly bound to an item property value, but are instead generated using a function, for example relying on the item data (represented by a PropertyBox) for each listing row (i.e. for each item instance).

Since the VirtualProperty provides its value through a PropertyValueProvider, the provider function is invoked when the listing column cells are rendered to obtain the column content to display, providing the current item/row value as a PropertyBox instance.

The generated column content type will match the VirtualProperty type, so if it is not of String type a suitable renderer should be configured to consistently display the column contents. See Columns rendering to learn how to configure column renderers and Using VirtualProperty for Component type columns.

A VirtualProperty type column can be added to a PropertyListing component in the following ways:

1. Include the VirtualProperty in the listing property set:

When one or more VirtualProperty type property is included in the default PropertyListing property set, a column bound to each VirtualProperty is included in the listing by default and no further configuration is required.

In this scenario, the VirtualProperty will be part of the item property set and available as item property through the PropertyBox item representation.

final NumericProperty<Long> ID = NumericProperty.longType("id");
final StringProperty TEXT = StringProperty.create("txt");

final VirtualProperty<String> DESCRIPTION = VirtualProperty.create(String.class,
    item -> "ID: " + item.getValue(ID)); (1)

PropertyListing listing = Components.listing.properties(ID, TEXT) //
    .build(ID, TEXT, DESCRIPTION); (2)
1 A String type VirtualProperty which uses the ID item property value to provide the generated value
2 The DESCRIPTION virtual property is included in the PROPERTIES listing property set, so it will be included and displayed as a column by default

2. Provide a VirtualProperty as a visible column:

When a VirtualProperty is declared within the visible columns list through the appropriate build(…​) methods, it will be automatically configured as listing column even if it is not part of the item listing property set.

final NumericProperty<Long> ID = NumericProperty.longType("id");
final VirtualProperty<String> DESCRIPTION = VirtualProperty.create(String.class,
    item -> "ID: " + item.getValue(ID)); (1)

final PropertySet<?> PROPERTIES = PropertySet.of(ID, DESCRIPTION);

PropertyListing listing = Components.listing.properties(PROPERTIES).build(); (2)
1 A String type VirtualProperty which uses the ID item property value to provide the generated value
2 The DESCRIPTION virtual property is included in the visible properties list, so it will be automatically included and displayed as a column

3. Use the withVirtualProperty(…​) builder API methods:

The PropertyListing builder API provide a set of convenience methods to add and configure a generated column using a VirtualProperty.

The withVirtualProperty(…​) methods accept the property type and the property value provider function (or an explicit VirtualProperty reference) and provide a specific builder API to configure the column which corresponds to the virtual property before adding it to the listing through the add() method.

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .withVirtualProperty(String.class, item -> "ID: " + item.getValue(ID)).add() (1)
    .withVirtualProperty(item -> "ID: " + item.getValue(ID)).add() (2)
    .build();
1 Add a String type virtual property providing the property value provider function and using add() to include the generated column in the listing
2 For String type virtual properties, a convenience shorter method is available

The virtual property column builder API provides a set of configuration methods to configure the column header, the width, the visibility, the style generator, the column renderer and the sorting behaviour:

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .withVirtualProperty(String.class, item -> "ID: " + item.getValue(ID)) (1)
    .header("The header") (2)
    .headerHTML("The <strong>header</strong>") (3)
    .alignment(ColumnAlignment.CENTER) (4)
    .width(100) (5)
    .minWidth(50) (6)
    .maxWidth(200) (7)
    .expandRatio(1) (8)
    .resizable(true) (9)
    .hidable(true) (10)
    .hidden(false) (11)
    .hidingToggleCaption("Show/hide") (12)
    .style("my-style") (13)
    .style((property, item) -> "stylename") (14)
    .render(new HtmlRenderer()) (15)
    .sortUsing(ID) (16)
    .sortGenerator((property, asc) -> QuerySort.of(ID, asc)) (17)
    .add() (18)
    .build();
1 Declare a String type virtual property providing the property value provider function
2 Configure the column header
3 Configure the column header as HTML
4 Configure the column alignment
5 Configure the column width
6 Configure the column minimum width
7 Configure the column maximum width
8 Configure the column expand ratio
9 Set the column as resizable
10 Set the column as hidable
11 Set the column as not hidden
12 Configure the column hiding toggle caption
13 Provide a fixed column CSS style class name
14 Provide the column CSS style class provider
15 Set the column renderer
16 Declare to use the ID property to implement the column sorting behaviour
17 Provide a column sorting generator
18 Add the virtual property column to the listing

By default, the column added using the withVirtualProperty(…​) methods are appended at the end of the listing columns. To control the virtual property column positioning within the listing columns, the following builder API methods can be used:

  • displayAsFirst(): Display the virtual property column before any other listing column.

  • displayAsLast(): Display the virtual property column after any other listing column.

  • displayBefore(Property property): Display the virtual property column before the listing column represented by the property property id.

  • displayAfter(Property property): Display the virtual property column after the listing column represented by the property property id.

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .withVirtualProperty(String.class, item -> "ID: " + item.getValue(ID)) (1)
    .displayAsFirst() (2)
    .displayAsLast() (3)
    .displayBefore(DESCRIPTION) (4)
    .displayAfter(ID) (5)
    .add().build();
1 Declare a String type virtual property providing the property value provider function
2 Display the virtual property column before any other listing column
3 Display the virtual property column after any other listing column
4 Display the virtual property column before the listing column bound the DESCRIPTION property
5 Display the virtual property column after the listing column bound the ID property

When a virtual property column has to be positioned relative to another virtual property column, you need a property id to which to refer. For this reason, specialized withVirtualProperty(…​) methods which accept the virtual property name are available.

Furthermore, the displayBeforeColumnId(String columnId) and displayAfterColumnId(String columnId) builder API methods can be use to display a virtual property column before or after another virtual property column with an assigned name.

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .withVirtualProperty(String.class, "_myid", item -> "ID: " + item.getValue(ID)).displayAfter(ID).add() (1)
    .withVirtualProperty(String.class, item -> "ID2: " + item.getValue(ID)).displayBeforeColumnId("_myid") (2)
    .add().build();
1 Declare a String type virtual property providing the property value provider function and _myid as virtual property name
2 Declare another virtual property column and display it before the _myid virtual property

9.2.6. Using VirtualProperty for Component type columns

When a Property of the PropertyListing property set is declared as com.vaadin.ui.Component type, the listing configures by default a suitable renderer to display the property value as a Vaadin Component, instead of a String.

When a listing Component type column has to be dinamically generated when the listing is rendered, the VirtualProperty property type can be used.

Since the VirtualProperty provides its value through a PropertyValueProvider, the provider is invoked when the listing column cells are rendered to obtain the Component to display, providing the current row values as PropertyBox, which can be used for component generation.

The VirtualProperty can be declared in the listing property set as any other Property, but when it is only used for a specific item listing column to render a Component it can be only declared as visible column, in the build(…​) method. This way, it will not be part of the listing property set (and it will not be included in the row PropertyBox properties) and will only be used as generated column.

final VirtualProperty<Component> EDIT = VirtualProperty.create(Component.class).message("Edit") (1)
    .valueProvider( (2)
        row -> Components.button().styleName(ValoTheme.BUTTON_ICON_ONLY).icon(VaadinIcons.EDIT)
            .onClick(e -> {
              Long rowId = row.getValue(ID); (3)
              // perform edit action ...
            }).build());

PropertyListing listing = Components.listing.properties(PROPERTIES) (4)
    .build(EDIT, ID, DESCRIPTION); (5)

listing = Components.listing.properties(PROPERTIES) //
    .build(PropertySet.builder().add(PROPERTIES).add(EDIT).build()); (6)
1 Declare a VirtualProperty of Component type. The configured message will be used as column header.
2 The property value provider returs a Button component
3 Since the row PropertyBox is provided at column cell generation time, we can use it to obtain a property value for the current row. In this example, the ID property value is obtained.
4 The listing property set do not includes the EDIT virtual property
5 The EDIT virtual property is declared as visible column, in the first position in this case
6 Another example in which the visible columns property set is composed joining the PROPERTIES set and the EDIT virtual property

9.3. BeanListing

A BeanListing implementation can be obtained using the Components API, through the listing.items method.

The Bean class to use as items type must be provided as construction time.

The Holon Platform Bean introspection API is used to detect the Bean class properties to use as listing columns and the Bean property names are used as column ids.

All the suitable bean property configuration attributes supported by the Holon Platform Bean introspection API are used to automatically configure the listing component, for example to set the column headers using the localizable property captions.

See the Bean property configuration section to learn about the Bean introspection API configuration capabilities and the available builtin annotations which can be used to provide a set of configuration attributes, such as @Caption or @Sequence.

For example, given the following bean class:

private class TestData {

  private Long id;
  private String description;

  // getters and setters omitted

A BeanListing which uses the bean class can be obtained as follows:

BeanListing<TestData> listing = Components.listing.items(TestData.class) (1)
    .build();
1 Create a BeanListing using TestData as items data type
See the Item listing API and configuration to learn about the item listing API operations and configuration options.

9.3.1. Bean items data source

The data source to provide the bean items for a BeanListing can be configured using the dataSource builder API methods.

1. Using an ItemDataProvider as items data source:

The ItemDataProvider API can be used as items data source.

For example, given a List of TestData bean instances, an in-memory ItemDataProvider can be configured in the following way:

final List<TestData> data = Arrays.asList(new TestData(1, "One"), new TestData(2, "Two"),
    new TestData(3, "Three"));

BeanListing<TestData> listing = Components.listing.items(TestData.class) //
    .dataSource(ItemDataProvider.create(cfg -> data.size(), (1)
        (cfg, offset, limit) -> data.stream().skip(offset).limit(limit)))
    .build();
1 Set an ItemDataProvider which uses the data List as items data source

2. Using a Datastore as items data source:

A Datastore implementation can be used as items data source to obtain the bean items from a persistence source.

The properties which will be used to perform query operations using the Datastore API will be the Bean properties obtained as a BeanPropertySet using the Holon Platform Bean introspection API.

final Datastore datastore = getDatastore();

BeanListing<TestData> listing = Components.listing.items(TestData.class) //
    .dataSource(datastore, DataTarget.named("test")) (1)
    .build();
1 Use a Datastore and given DataTarget as items data source

The BeanListing API makes available a refresh() method to refresh the current items set, causing items reloading from the items data source.

BeanListing<TestData> listing = Components.listing.items(TestData.class) //
    .dataSource(getDatastore(), DataTarget.named("test")).build();

listing.refresh(); (1)
1 Refresh the items set

9.3.2. Dynamically generated columns

The BeanListing component supports virtual columns definition to declare generated column types, which contents are generated when the listing is rendered, for example to provide Component type column contents.

The withVirtualColumn(String id, Class<V> type, ValueProvider<T, V> valueProvider) method of the BeanListing builder API allows to declare a virtual column, providing:

  • The virtual column id and type

  • A ValueProvider which will be used to generate the column cells, using the row item bean instance

The declared virtual column id can later be used to include the virtual column in the visible column in the required position.

BeanListing<TestData> listing = Components.listing.items(TestData.class)
    .withVirtualColumn("delete", Button.class, bean -> { (1)
      return Components.button().icon(VaadinIcons.TRASH).onClick(e -> {
        Long rowId = bean.getId(); (2)
      }).build();
    }).build("delete", "id", "description"); (3)
1 Declare a virtual column of Button type and delete as column id
2 The bean instance of the current row can be used to obtain row values, for example the id attribute in this case
3 Declare the delete column as visible and render it as first column

9.4. Item listing API and configuration

The item listing component builder API provides a wide set of configuration methods which can be used to:

  • Configure the listing columns, header and footer sections.

  • Register a number of listeners for different event types.

  • Configure the items data source query.

See the next sections for details.

9.4.1. Columns configuration

The listing component columns can be configured in a number of ways. Below is provided an example which uses a PropertyListing and shows the available column configuration methods:

PropertyListing listing = Components.listing.properties(PROPERTIES) //
    .header(ID, "Custom ID header") (1)
    .header(ID, "Default header", "id-header-message-code") (2)
    .headerHTML(ID, "HTML <strong>header</strong>") (3)
    .columnHidingAllowed(true) (4)
    .hidable(ID, false) (5)
    .columnReorderingAllowed(true) (6)
    .alignment(ID, ColumnAlignment.RIGHT) (7)
    .hidden(DESCRIPTION, true) (8)
    .resizable(ID, false) (9)
    .width(ID, 100) (10)
    .expandRatio(DESCRIPTION, 1) (11)
    .minWidth(DESCRIPTION, 200) (12)
    .maxWidth(DESCRIPTION, 300) (13)
    .minimumWidthFromContent(DESCRIPTION, true) (14)
    .style(ID, (property, item) -> item.getValue(DESCRIPTION) != null ? "empty" : "not-empty") (15)
    .withPropertyReorderListener((properties, userOriginated) -> { (16)
      // ...
    }).withPropertyResizeListener((property, widthInPixel, userOriginated) -> { (17)
      // ...
    }).withPropertyVisibilityListener((property, hidden, userOriginated) -> { (18)
      // ...
    }).build();
1 Set a custom header caption for the ID property/column
2 Set the ID property/column localizable header caption providing the default message and the localization message code (See the Internationalization documentation for further information about localization)
3 Set a the header caption for the ID property/column using HTML markup
4 Set whether the listing columns can be hidden
5 Set that the ID property/column cannot be hidden
6 Set whether the listing columns can reordered
7 Set the ID property/column cell contents alignment
8 Set the DESCRIPTION property/column as hidden by default
9 Set the ID property/column as not resizable
10 Set the ID property/column width in pixels
11 Set the DESCRIPTION property/column expand ratio
12 Set the DESCRIPTION property/column minimum width
13 Set the DESCRIPTION property/column maximum width
14 Set to use the width of the contents in the column as DESCRIPTION property/column width
15 Set the ID property/column CSS style generator
16 Add a listener to be notified when the listing columns order changes
17 Add a listener to be notified when a property/column is resized
18 Add a listener to be notified when a property/column is shown or hidden

9.4.2. Columns rendering

By default, the cells of a column bound to a Property are rendered using the Holon Platform presenters available from the default registry, to provide the String representation of the cell value.

See the String value presenters documentation for details.

The default column rendering can be overridden using a custom Vaadin Renderer. The provided 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, value -> String.valueOf(value), new TextRenderer()) (2)
    .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

9.4.3. Item 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, index, 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

9.4.4. Items selection

The item listing component supports items selection, both in single and multiple mode. The listing can be made selectable by 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
The select all checkbox shown in the top left corner of the listing is visible by default. You can control the checkbox visibility using the selectAllCheckBoxVisibility(…​) method of the item listing builder.

9.4.5. Items 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 be always 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

9.4.6. 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 items 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

9.4.7. 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

9.4.8. 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 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)
    .editorBuffered(true) (2)
    .editorSaveCaption("Save item") (3)
    .editorCancelCaption("Discard") (4)
    .editable(ID, false) (5)
    .editor(DESCRIPTION, new TextField()) (6)
    .withValidator(Validator.create(pb -> pb.getValue(DESCRIPTION) != null, "Description must be not null")) (7)
    .withValidator(DESCRIPTION, Validator.max(100)) (8)
    .required(ID) (9)
    .build();
1 Set the listing as editable
2 Set the row editor in buffered mode
3 Set the editor save button caption
4 Set the editor cancel button caption
5 Set a property as not editable
6 Set a custom editor field
7 Add an overall value validator
8 Add a property value validator
9 Set the ID property as required, automatically adding a not empty validator

10. 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(VaadinIcons.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

10.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

11. 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 Field property renderer, which represents a HasValue Vaadin Component

  • 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 (HasValue component)
3 Render the property as String type ViewComponent

11.1. Property localization

When a LocalizationContext is available as a context resource, it is used to localize any element to display which supports localization.

See the LocalizationContext documentation for details about the LocalizationContext API and the Context resources documentation for information about the Holon Platform Context architecture.

Concerning to a Property related UI component, the LocalizationContext 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 an input field

  • Obtain the number format to be used to display a property value or to edit the property value through an input field

  • Obtain the default boolean localized text for the true and false values

See the Internationalization documentation for further information about localization.

11.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)
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

12. 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

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

});

14. Navigator

Maven coordinates:

<groupId>com.holon-platform.vaadin</groupId>
<artifactId>holon-vaadin-navigator</artifactId>
<version>5.2.5</version>

The holon-vaadin-navigator artifact makes available an extension of the default Vaadin view navigator, represented by the ViewNavigator API.

The ViewNavigator API, compared to the standard Vaadin Navigator one, 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 a 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.

14.1. View 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 (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;

}
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

14.2. View lifecycle management

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 is present in the View class or in its 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)
    // ...
  }

}
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

14.3. Providing the View contents

By default, the View is rendered as the UI component which is represented by the View class.

You can override the standard Vaadin 8 getViewComponent() method of the View class to control which Component to provide as View content.

class ViewExampleContent extends VerticalLayout implements View { (1)

  public ViewExampleContent() {
    super();
    addComponent(new Label("View content"));
  }

}

class ViewExampleContentProvider implements View {

  @Override
  public Component getViewComponent() { (2)
    boolean mobile = DeviceInfo.get().map(info -> info.isMobile()).orElse(false);
    return mobile ? buildMobileViewContent() : buildDefaultViewContent();
  }

}
1 Default View implementation extending a Component class (a VerticalLayout). The displayed View content will be the component itself
2 Provide the View content using the getViewComponent() method. 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

14.4. ViewNavigator Configuration

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

  • A ViewProvider to provide the 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(e -> { (9)
          // ...
          return true;
        }).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

14.5. Excluding a View form navigation history

By default, the ViewNavigator tracks 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 {

}

14.6. Opening Views in a Window

The ViewNavigator API supports displaying a View content in an application Window. Instead of using the configured view display component, the View contents will be displayed in a popup dialog, using the com.vaadin.ui.Window component.

The view navigation state is tracked in the navigator history just like any other navigation operation.

To open a View in a Window, the navigateInWindow(…​) view navigator methods can be used.

ViewNavigator.require().navigateInWindow("myView"); (1)
1 Opens the View named myView in a Window

14.6.1. @WindowView annotation

The WindowView annotation can be used to declare that a View should always be displayed in a Window by the ViewNavigator.

When 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.

@WindowView (1)
class MyWindowView1 implements View {

}

void openTheView() {
  ViewNavigator.require().navigateTo("myView"); (2)
}
1 Configure the View class to be always displayed in a Window
2 Supposing that the MyWindowView1 class is bound to the myView name, the ViewNavigator will display such View in a Window since the @WindowView annotation is found on the view class

14.6.2. View Window configuration

When a View is displayed in a Window, the Window component which contains the View can be configured in the following ways:

1. Using the provided ViewWindowConfigurator: The ViewNavigator API provides a navigateInWindow(…​) method version which provides a ViewWindowConfigurator API to configure the Window component into which the View will be displayed.

ViewNavigator.require().navigateInWindow("myView", configurator -> {
  configurator.fullWidth().caption("Title").closable(false).resizable(false); (1)
});
1 Use the ViewWindowConfigurator API to configure the Window component

2. Using the @WindowView annotation attributes: When a View is configured to be displayed in a Window using the @WindowView annotation, the available annotation attributes can be used to configure some basic Window component features.

@WindowView(windowWidth = "50%", windowHeigth = "50%", closable = false, resizable = true) (1)
class MyWindowView2 implements View {

}
1 Use the @WindowView annotation attributes to configure the view Window

3. Using a @ViewWindowConfiguration annotated View method: If a View class public method is annotated with the @ViewWindowConfiguration annotation and accepts a single parameter of ViewWindowConfigurator type, the method is called by the ViewNavigator, providing the ViewWindowConfigurator instance which can be used to configure the view Window component.

@WindowView
class MyWindowView3 implements View {

  @ViewWindowConfiguration (1)
  public void configure(ViewWindowConfigurator configurator) {
    configurator.fullWidth().caption("Title").closable(false).resizable(false);
  }

}
1 This method will be called by the ViewNavigator before displaying the View, allowing to configure the view Window component using the provided ViewWindowConfigurator

14.7. ViewNavigator API operations

The ViewNavigator API makes available 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 View 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.

14.8. 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, then 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(view.getViewComponent(), viewName, VaadinIcons.PIN);
    return true;
  }

  @Override
  public View getCurrentView() {
    return (View) getSelectedTab();
  }

}

@SubViewOf("mycontainer")
public class SubViewExample extends VerticalLayout implements View { (2)

  public SubViewExample() {
    super();
    setMargin(true);
    addComponent(new Label("The sub view 1"));
  }

}
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

14.9. 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;

}

14.10. Obtain the current 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

14.11. 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).

In order for the View authentication process to work properly, when the @Authenticate annotation is used on one or more specific View class (and not at UI level), a ViewClassProvider instance has to be bound to each registered ViewProvider to provide the View class which corresponds to a specific view name. The ViewNavigator builder API provides the viewClassProvider(…​) method to configure the ViewClassProvider. When the Spring integration is enabled and the default SpringViewNavigator is used, a default ViewClassProvider is automatically configured, without needing to configure it explicitly.

15. Spring integration

The holon-vaadin-spring artifact provides support and integration with the Spring framework.

Maven coordinates:

<groupId>com.holon-platform.vaadin</groupId>
<artifactId>holon-vaadin-spring</artifactId>
<version>5.2.5</version>

This artifact provides a ViewNavigator extension with Spring support, represented by the SpringViewNavigator API.

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.

15.1. Default View navigation strategy

When a statically declared default View (using for example the @DefaultView annotation) is not suitable for an application, the DefaultViewNavigationStrategy interface can be used to implement a custom strategy to provide the default navigation state.

When a DefaultViewNavigationStrategy type Spring bean is found in application context, it is automatically configured in View navigator as default view navigation stategy implementation.

15.2. 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 register 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 {

}

@SpringView(name = "view2") (7)
@UIScope (8)
class ViewTwo extends VerticalLayout implements View {

}

@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

15.3. 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.

15.4. 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 Spring context scope 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 {

}

@SpringView(name = "view2")
@RolesAllowed("role1") (5)
class ViewTwo extends VerticalLayout implements View {

}

@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

15.5. Spring Security support

The View access control using the Spring Security @Secured annotation can be enabled using the EnableSecuredView annotation.

This access control strategy relies on the Spring Security SecurityContext to check current authentication and performs View access control checking current user granted authorities against the security attributes specified through the @Secured annotation.

The Spring Security artifacts must be explicitly included as a project dependency.

16. 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.vaadin</groupId>
<artifactId>holon-vaadin-spring-boot</artifactId>
<version>5.2.5</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.

  • If Spring Security is available and configured, the @Secured annotation based View access control is enabled.

To disable this auto-configuration feature the HolonVaadinAutoConfiguration class can be excluded:

@EnableAutoConfiguration(exclude={HolonVaadinAutoConfiguration.class})

To disable the Spring Security @Secured access control support the HolonVaadinSpringSecurityAutoConfiguration class can be excluded:

@EnableAutoConfiguration(exclude={HolonVaadinSpringSecurityAutoConfiguration.class})

16.1. Vaadin servlet configuration

By default, a Holon extension of the Vaadin servlet is auto-configured.

This servlet allows automatic com.vaadin.server.SessionInitListener and com.vaadin.server.SessionDestroyListener registration if they are defined as Spring beans and made available in the Spring context.

To disable this auto-configuration feature, the HolonVaadinServletAutoConfiguration class can be excluded:

@EnableAutoConfiguration(exclude={HolonVaadinServletAutoConfiguration.class})

16.2. Spring Boot starters

The following starter artifacts are available to provide a quick project configuration setup using Maven dependency system:

1. The Vaadin application starter provides the dependencies to the Holon Platform Vaadin Spring Boot integration artifact holon-vaadin-spring-boot, in addition to:

See the Spring Boot starters documentation for details on Spring Boot starters.

Maven coordinates:

<groupId>com.holon-platform.vaadin</groupId>
<artifactId>holon-starter-vaadin</artifactId>
<version>5.2.5</version>

2. The Vaadin application starter using Undertow provides the same dependencies as the default Vaadin application starter, but using Undertow instead of Tomcat as embedded servlet container.

Maven coordinates:

<groupId>com.holon-platform.vaadin</groupId>
<artifactId>holon-starter-vaadin-undertow</artifactId>
<version>5.2.5</version>

17. 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.

18. System requirements

18.1. Java

The Holon Platform Vaadin module requires Java 8 or higher.

18.2. Vaadin

The Holon Platform Vaadin module requires Vaadin 8.1 or higher.