UI Components

UI Elements

Both fields and buttons are "UI elements". The following types are provided:

Table 1. Fields

UILabel

Displaying of text

UILink

Hyperlink

UITextField

Field for single line text entry

UITextArea

Area for multiline text entry

UIIntegerField, UIDoubleField

Field for numbers

UICheckbox

Control for boolean input

UIDateField

Field for date entry with date picker

UITimeField

Field for time entry with time picker

UIDateTimeField

Field for date and time entry with date time picker

UIComboBox

Dropdown field with predefined options

UIMultiSelect

Dropdown field with predefined options for selecting multiple options

UIYesNoCombobox

Dropdown field for boolean choices

UIRadioButtons

Radio buttons for selecting a single choice

Table 2. Buttons

UIButton

A button that triggers an action when clicked

The type of a field can be determined dynamically. This mechanism is called dynamic field.

Annotations for fields and buttons must exist on methods in a PMO class. The main difference between fields and buttons is that fields are used for displaying and editing of values and thus are bound to a value via data binding. Therefore annotations for fields must exist on a getter method of a PMO property. Only if domain model binding is used the method can be named differently.

Buttons are not bound to a value, but to an annotated method. It represents an executable action which is called by the button click.

Properties

All UI elements have the position property. In addition, many of the annotations have some aspects embedded, such as

Fields have additional properties that are required for the domain model binding:

position must always be specified. For most annotations, label is also mandatory. All other properties have default values.
Position

The "position" property defines the order of elements in the UI. The relative size of the value is deciding. Elements with smaller position are added to a section first.

Gaps in the position numbering are allowed and common, to allow adding new UI elements at a later moment without needing to renumber all elements.
ModelObject and -Attribute

A field can be bound to an attribute of an existing model object using domain model binding. For this the properties modelAttribute and possibly modelObject are required. The function is described in the section names of model attributes.

UILabel

The annotation @UILabel generates an independent UI element displaying String content. In contrast to a deactivated UITextField, its text is not framed by an input field.

The annotated method must return a value that is convertable to String. This can either be achieved by a registered converter at LinkkiConverterRegistry, or by a ItemCaptionProvider that can be provided to the UILabel annotation.

If the Faktor-IPS linkki extension is used, the FormattedStringToDecimalConverter should be registered to LinkkiConverterRegistry in order to display Faktor-IPS Decimal values property.

A @UILabel also has the additional property label. If a value is set to it the text would appear beside or on top of the UILabel.
When used in a table, the label is used as the column name while the caption is displayed in the table cell.
HTML Content

The label’s content can be enhanced with HTML when the annotated method returns an HtmlContent object. The HtmlContent class provides access to a builder for creating HTML content. For common use cases, static methods are available to simplify HtmlContent creation. If HTML content from the model needs to be displayed, the HtmlContent.sanitize(String) method can be used to generate a sanitized HtmlContent instance based on the model’s value. An example below illustrates how to create multicolored text.

Example HTML Label Content
            @UILabel(position = 10, label = "Label with HtmlContent")
            @BindIcon(value = VaadinIcon.ACCORDION_MENU)
            @BindSuffix("%")
            public HtmlContent getHtmlContentLabel() {
                return HtmlContent.builder()
                        .styledTag("i", "color: red;", "HTML")
                        .text(" ")
                        .tag("b", "Content")
                        .build();
            }

A common usecase is to display a string split accross multiple lines. This can be achieved by using the HtmlContent.multilineText(String …​) method. If you are building an extendable PMO and it may be reasonable for subclasses to use HTMLContent, make sure that the method returns HtmlContent.

Styles

To style labels the property styleNames can be used to specify a list (actually a String[]) of CSS class names.

Icon position

The icon position can be set with the property iconPosition. An icon can be placed to the left (default) or to the right of the label.

The annotation @UILink creates a hyperlink. In comparison to a button styled as a link, a link in Vaadin has the advantage that the user can use the context menu to follow the link in a separate browser tab or to copy the link. The caption can also be easily copied.

The annotated method should return the link URL as a string.

The annotation has the following attributes:

  • caption: String

  • captionType: CaptionType

  • target: String: Defines where to open the link as specified by HTML. The four predefined targets BLANK, SELF, PARENT and TOP can be found in the constant class LinkTarget as well as the empty String constant LinkTarget.DYNAMIC, which leads to dynamic target resolution via a get<PropertyName>Target method.

A @UILink also has the additional property label. If a value is set to it the text would appear beside the link.
When used in a table, the label is used as the column name while the caption is displayed as the link text in the table cell.
Icon position

The icon position can be set with the property iconPosition. An icon can be placed to the left or to the right (default) of the link.

UICheckbox

The annotation @UICheckbox creates a com.vaadin.flow.component.checkbox.Checkbox that is bound to a boolean property.

Instead of a label on the left side of the UI element, checkboxes usually have a caption on the right. This caption must be set with the property caption. If no caption is desired, an empty String must be set.

The usual label property is still available if any display text is needed on the left side additionally.
When used in a table, the label is used as the column name while the caption is displayed next to the checkbox in the table cell.

UITextField

The annotation UITextfield corresponds to a com.vaadin.flow.component.textfield.TextField for text entry. It has two additional properties:

  • maxLength: int

  • width: String

maxLength defines the maximum number of characters that can be entered or displayed in the field while width defines the visible width of the field using a number and a CSS unit, for example "5em" or "50%". The width is set to "100%" by default which means it grabs all available space.

UITextArea

The annotation UITextArea corresponds to a com.vaadin.flow.component.textfield.TextArea. It is used for entering or displaying text that has more than one line. UITextArea has all the properties of the annotation UITextfield. In addition, it also has:

  • height: String

The property height defines the height of the UITextArea, not how many rows can be entered. It returns a String using a number and a CSS unit, for example "5em". Its default value is 3em.

UIIntegerField and UIDoubleField

The annotations @UIIntegerfield and @UIDoubleField are text fields for displaying formatted numbers. Like @UITextField these annotations have the property maxLength.

The format can be defined with the property format: String, using the notation from java.text.NumberFormat.

If no format is specified for a UIIntegerField, linkki uses the default Java Integer NumberFormat (java.text.NumberFormat#getIntegerInstance(java.util.Locale)). In the case of UIDoubleField the format #,##0.00## is used by default. This format means that at least one digit is displayed before the decimal separator and two after, and the thousands separator is displayed as well. The documentation for the format definition can be looked up in the class java.text.DecimalFormat.

UIDateField

The @UIDateField annotation creates a date input field and is equivalent to com.vaadin.flow.component.datepicker.DatePicker. The date is formatted according to the language preference set in the user’s browser. For example, using the German Locale will format the date as dd.mm.yyyy. The UIDateField allows multiple date formats to be set, and by default uses the same date format as the standard Vaadin date field, but also allows dates to be entered without punctuation, so for example 010420 becomes 01.04.2020.

UITimeField

The annotation @UITimeField generates a time input field and corresponds to com.vaadin.flow.component.datepicker.TimePicker. Values typed into the time field will automatically be converted to the format matching the Locale setting. E.g. 15:00 becomes 3:00 PM when the Locale is English.

It has one additional property:

  • step: long

This property defines the time interval in minutes between the items displayed in the time picker overlay. It also specifies the amount by which the time increases/decreases using the Up/Down arrow keys when the overlays are disabled. Its default value is 60.

  • precision: ChronoUnit

The precision property specifies the unit of granularity for the time value, such as minutes or seconds. By default, this is set to minutes.

It is noted that the overlay for time selection is disabled when precision is configured with a step size resulting in intervals smaller than 15 minutes. This is to prevent an impractical number of choices from being displayed. Manual entry of time, including seconds, is permitted and processed accordingly. It is also possible to adjust the time using the up and down arrow keys.

The step must divide an hour or day evenly. For example, 15, 30 and 60 are valid steps, while 42 and 300 are not. If an invalid value is used, an exception will be thrown by Vaadin.

UIDateTimeField

The annotation @UIDateTimeField generates a date and time input field and corresponds to com.vaadin.flow.component.datetimepicker.DateTimePicker. It is used for selecting both a date and a time of day. UIDateTimeField has all the properties of the annotations UIDateField and UITimeField.

UIComboBox

The annotation @UIComboBox allows selection of a value from a list and corresponds to com.vaadin.flow.component.combobox.ComboBox. It has three additional properties:

    @UIComboBox(position = 20,
            label = "Model",
            modelAttribute = Car.PROPERTY_MODEL,
            required = RequiredType.REQUIRED_IF_ENABLED,
            content = AvailableValuesType.DYNAMIC,
            itemCaptionProvider = ToStringCaptionProvider.class)
    public void model() {
        /* model binding */
    }

To style items in the combo box popup menu the annotation @BindComboBoxItemStyle can be used. To handle refresh of items when using dynamic captions @BindComboBoxDynamicCaption can be applied.

Content

The attribute content defines which values are available:

Table 3. AvailableValuesType

ENUM_VALUES_INCL_NULL

the values of the combobox correspond to the values of the enum data type of the property, extended by the value null (default). It can also be used for boolean.

ENUM_VALUES_EXCL_NULL

the values of the combobox correspond to the values of the enum data type of the property. It can also be used for boolean.

DYNAMIC

the values of the combobox are defined dynamically through the method Collection<T> get<PropertyName>AvailableValues()

NO_VALUES

this combobox has no selectable values

Width

The property width can be used to define the width of the combobox using CSS syntax (e.g. "25em" or "100%"). The default value is -1px, corresponding to the standard size given by Vaadin.

ItemCaptionProvider

A org.linkki.core.defaults.ui.element.ItemCaptionProvider<T> is used to display the individual values in the combobox. By default, it is a DefaultCaptionProvider which determines the caption by trying to call the methods getName(Locale), getName, toString in that order. Boolean values are translated to German or English depending on the locale of the UI.

An alternative implementation class can be specified via the property itemCaptionProvider. linkki offers two additional ones:

  • ToStringCaptionProvider: uses the `toString()`method of the elements

  • IdAndNameCaptionProvider in linkki-ips: displays name and ID in the format "name [ID]" using the methods getName() and getId().

TextAlign

The property textAlign specifies the text alignment of the value inside the combo box, and the values in the drop-down menu. RIGHT is recommended for numeric values. If custom styles are used via @BindComboBoxItemStyle, the textAlignment will not affect the drop-down list. Instead, an appropriate CSS class has to be set by @BindComboBoxItemStyle.

Automatically focus first applicable item

By default, a Vaadin ComboBox only accepts text input that exactly matches an item. For example, in a combo box with the options "aa", "bb", typing in "a" would not select "aa", although it is the only one that matches the input.
To improve this behavior, linkki provides Javascripts that can be used to enhance the usability. If application framework is used, these scripts are executed automatically. Otherwise, these need to be imported by using @JsModule. Ideally on a parent route layout that is used by all routes.

@JsModule("./src/focus-first-item-combo-box-mixin.js")
@JsModule("./src/focus-first-item-combo-box-scroller.js")
@Route("my")
public class MyView extends Div {}

UIMultiSelect

The annotation @UIMultiSelect allows selection of multiple values from a list and corresponds to com.vaadin.flow.component.combobox.MultiSelectComboBox. It has two properties known from the UIComboBox:

The getter and setter for the selected values have to be of type Set<T>. This implies that there is no guaranteed order of the selected values.

In contrast to a combobox, the values of the multi select box are always defined dynamically through the method Collection<T> get<PropertyName>AvailableValues(). Be aware that some subtypes of Collection have no guaranteed order of elements and should not be used. Otherwise, the list of available values of this component may have a different order in the UI which is not very user-friendly.

UIYesNoComboBox

This annotation is deprecated since 2.6.0. Use UIComboBox directly instead.

The annotation @UIYesNoComboBox allows selection of a boolean value from a dropdown list like a UIComboBox. The difference is that the values are not a generic enumeration or list but the well known boolean values true and false (and for Boolean, the option null). It has two properties known from the UIComboBox:

UIRadioButtons

The annotation @UIRadioButtons allows selection of a single value using a group of buttons and corresponds to com.vaadin.flow.component.radiobutton.RadioButtonGroup. Multiple values cannot be selected at the same time.

UICustomField

Other controls can also easily be generated and bound by linkki. For this the annotation @UICustomField is used.

The control class is specified with the property uiControl: Class<? extends Field<?>>. If the control implements com.vaadin.flow.data.provider.HasListDataView<T, V> the values can be defined by content: AvailableValuesType like with UIComboBox.

@UICustomField only supports controls with a parameter-less constructor.
UICustomField Example: PasswordField
    @UICustomField(position = 0, label = "Secret", uiControl = PasswordField.class)
    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

Dynamic Field

linkki allows for dynamic typing of an input field. In the following example Retention should only be freely writable if CarType is set to STANDARD. Otherwise, the user can only select values from a list:

    @UIDoubleField(position = 30,
            label = "Retention",
            modelAttribute = Car.PROPERTY_RETENTION,
            required = RequiredType.REQUIRED_IF_ENABLED)
    @UIComboBox(position = 30,
            label = "Retention",
            modelAttribute = Car.PROPERTY_RETENTION,
            required = RequiredType.REQUIRED_IF_ENABLED,
            content = AvailableValuesType.DYNAMIC,
            itemCaptionProvider = RetentionCaptionProvider.class)
    public void retention() {
        /* model binding */
    }

    public List<Double> getRetentionAvailableValues() {
        return Arrays.asList(2_000.0, 5_000.0, 10_000.0);
    }

    public Class<?> getRetentionComponentType() {
        return car.getCarType() == CarType.STANDARD ? UIDoubleField.class : UIComboBox.class;
    }

The selectable UI elements are defined via annotations on the method, as is customary. They must, however, fulfill the following requirements, to allow the type to be determined dynamically:

  1. the position in the UI* annotations must match

  2. the label must have the same value

If the position values are identical but the label values differ, an exception is thrown.

Which UI element is displayed for each PMO instance is determined by the method Class<?> get<PropertyName>ComponentType(). It returns the class of the UI*-Annotation for the UI control to be rendered.

UIButton

The annotation @UIButton is used to mark the method that should be executed when the button is clicked. It corresponds to com.vaadin.flow.component.button.Button.

Since buttons are not bound to values and therefore have no corresponding PMO property, the name of the annotated method is used to determine the associated methods. The behavior is similar to the domain model binding, although buttons have no property modelAttribute.

    @UIButton(position = 10,
            showIcon = true,
            icon = VaadinIcon.CHECK, //
            captionType = CaptionType.NONE,
            enabled = EnabledType.DYNAMIC,
            shortcutKeyCode = KeyCode.ENTER,
            variants = ButtonVariant.LUMO_PRIMARY)
    public void save() {
        saveAction.apply();
    }

    public boolean isSaveEnabled() {
        return canSaveSupplier.getAsBoolean();
    }

    @UIButton(position = 20,
            captionType = CaptionType.STATIC,
            caption = "reset",
            variants = ButtonVariant.LUMO_TERTIARY)
    public void reset() {
        resetAction.apply();
    }

Apart from the common properties, buttons have these additional ones:

Caption

The text shown on a button is called a caption. It is not to be confused with a Label, which usually appears besides the control. A button can have both a caption and a label text.

  • captionType: CaptionType

Table 4. CaptionType

STATIC

the caption of the button is read from the attribute caption (default)

NONE

the button has no caption

DYNAMIC

the caption of the button is determined by the return value of the method String get<PropertyName>Caption(). The value of the attribute caption is ignored.

Icon

Apart from captions, buttons can be adorned with icons. For this the constants of the Vaadin class VaadinIcon are used. For the icon to be displayed the property showIcon must be set true.

Button Variants

Depending on the function buttons must be styled differently. com.vaadin.flow.component.button.ButtonVariant offers different theme variants, such as primary or inline. For more information please refer to Vaadin Button Variants Documentation.

Key Bindings

Some buttons shouldn’t be triggered only by mouse click, but also by key combinations. These can be specified with the properties shortcutModifierKeys and shortcutKeyCode. shortcutModifierKeys defines which keys must be pressed and held before the key in the shortcutKeyCode is pressed. For instance, in many applications saving is triggered with the shortcut combination "Ctrl + s". In this case the "Ctrl" key is the modifier and the "s" key is the shortcut key.

The appropriate value for modifiers can be found in KeyModifier. The most common keys are provided by the class KeyCode. Other values for keys can be deducted by the Vaadin class Keys.

As button shortcuts work globally, there should always be only one button that uses a key combination as shortcut.
In addition, a button that uses enter as shortcut should also use the variant PRIMARY to make it visibly clear to the user that the button would be triggered.
To prevent unexpected behaviors, defining a shortcut for a button results in preventing the browser default for that key combination. For example, if a button defines the enter key as shortcut, it will not be possible to create line break in text areas using enter. Shift + enter would still work if there is no button that uses shift + enter as shortcut.