UI Components

Aspects

linkki can bind more than just the value. In fact, almost every part of the UI state can be taken into account for data binding with linkki. The most common parts of the UI state are for example the enabled state or the visibility. But there are also additional features that may be bound to a component such as the tooltip, available values for selection or CSS class names. For a property, every such UI state part is a so called aspect of the property.

Some aspects are crucial for the functionality of the UI element and are thus directly embedded in the annotation. Besides those, linkki also provides some standalone aspect annotations that can be optionally applied to the UI elements.

Aspects in a PMO

The following example shows two aspects for the property "name".

Value and enabled aspect
    @UITextField(position = 1, label = "Name", enabled = DYNAMIC)
    public String getName() {
        return partner.getName();
    }

    public void setName(String name) {
        partner.setName(name);
    }

    public boolean isNameEnabled() {
        return partner.getType() == PartnerType.NATURAL_PERSON;
    }

The most important aspect is the value aspect. It is also mandatory for the data binding to function. The value aspect is dynamically determined by the getter and setter methods of the property. As the value aspect is critical for the UI element to function, it is always embedded in the UI element annotation.

The second aspect that can be seen is the enabled aspect. This state is defined by the method isNameEnabled.

Each aspect has a name. The enabled aspect has the name "enabled" whereas the value aspect has the empty String as name. In general, the state of the aspect "aspect" for property "property" is determined by the method is/get<Property><Aspect> and set<Property><Aspect>.

Aspects can also apply to the whole PMO if it is also bound to a UI component. In this case, the property name is the empty string.

Aspect on PMO class
@BindStyleNames
@UISection(caption = "Partner")
public class PartnerSectionPmo {

    public List<String> getStyleNames() {
        if (partner.getType() == PartnerType.NATURAL_PERSON) {
            return Arrays.asList("naturalperson");
        } else {
            return Collections.emptyList();
        }
    }
}

Commonly Embedded Aspects

Some aspects are so important or commonly used that they are directly packaged with the UI annotation. Many of those aspects offer configurations that can be set by attributes in the annotation. Most frequently, the attributes share the name of the aspect itself, e.g. @UITextField(enabled = …​) in case of the enabled aspect.

Below are the commonly embedded aspects.

Label

Usually there is a label text for each UI element, that describes the element. The content of the label is defined by this attribute.

Exceptions to this are elements whose main feature is their text, such as buttons, checkboxes and links. These UI elements usually do not need a preceding label. Instead, the description of a button is displayed on the button itself, the description of a link is displayed as the link text, while the description of the checkbox is commonly displayed at the right of the checkbox. This kind of describing element can be configured with the property caption in those annotations. Buttons, links and checkboxes can still be configured with a label additionally by overwriting the label property.

If no label/caption is set, the default value "derived.by.linkki" kicks in and linkki uses the default as determined by the PropertyDispatchers (usually the capitalized property name) as label/caption.

The label is used as the column caption in tables.

If an independent label is needed, the UI element UILabel can be used.
Enabled

The property enabled controls whether a component is enabled or disabled. The following configuration options are available:

Table 1. EnabledType

ENABLED

The content of the element is modifiable in the UI (default)

DISABLED

The content of the element is not modifiable in the UI

DYNAMIC

Whether the content is modifiable is controlled by the return value of the method boolean is<PropertyName>Enabled()

Some components like UILabel and UILink do not offer these options and are always enabled.
Visible

The property visible controls whether the component is visible. There are the following configuration options:

Table 2. VisibleType

VISIBLE

The UI element is visible (default)

INVISIBLE

The UI element is invisible

DYNAMIC

Whether the UI element is visible is controlled by the return value of the method boolean is<PropertyName>Visible()

Required

The property required visually highlights required fields. The following configuration options are available:

Table 3. RequiredType

REQUIRED

The UI element requires input (a value must be entered/selected)

REQUIRED_IF_ENABLED

The UI element requires input if it is enabled

NOT_REQUIRED

Input in the UI element is optional (default)

DYNAMIC

Whether the element requires input is controlled by the return value of the method boolean is<PropertyName>Required()

UILabels, UILinks and UIButtons do not offer these options and are never required.
Fields marked as required are only visually highlighted. No validation is performed.

Standalone Aspect Annotations

Aspects can also be represented by a separate annotation. In this case, those annotations follow the naming convention @Bind[AspectName]. A standalone aspect annotation can be added to an annotated PMO property. Some standalone aspect annotation can also be added to an annotated PMO class itself. In the case of binding using the @Bind annotation, @Bind[AspectName] must be written directly in the field annotated with @Bind. Several frequently used aspects are packaged with linkki as listed below. It is also very easy to create additional custom aspect annotations.

Standalone Annotation Description

@BindTooltip

Tooltip handling, using default TooltipType.AUTO

@BindReadOnly

Bind a component’s read-only property. Exclusive to Components of type HasValue

@BindReadOnlyBehavior

Automatically change enabled or visible behavior when in read-only mode.

@BindVisible

Visiblity handling, using default VisibleType.DYNAMIC

@BindStyleNames

Provide style class names for a component

@BindCaption

Caption handling, using default CaptionType.AUTO

@BindIcon

Icon handling, using default IconType.AUTO

@BindSuffix

Suffix handling, using default SuffixType.AUTO

@BindPlaceholder

Placeholder handling, using default PlaceholderType.AUTO

@BindAutoFocus

Bind the autofocus property of a UI element.

@BindVariantNames

Bind variant theme to a UI element or PMO class.

@BindMessages

Manually filter and set validation messages on a field.

@BindLabel

Set label texts dynamically.

@BindSlot

Sets a UI element into a slot of a predefined, reusable layout.

Tooltips

For displaying tooltips on UI elements linkki provides the annotation @BindTooltip.

The @BindTooltip annotation has two properties:

Value

This is the text displayed on TooltipType.STATIC. Its default value is an empty string ("").

TooltipType

The following configuration options are available for TooltipType:

Table 4. TooltipType

AUTO

The text of the tooltip is read from the attribute value if it is not empty, otherwise it reacts like DYNAMIC (default)

STATIC

The text of the tooltip is read from the attribute value

DYNAMIC

The text of the tooltip is determined by the return value of the method String get<PropertyName>Tooltip(). The value is ignored.

Tooltips can only be added to fields and buttons.
    @BindTooltip("Edit")
    @UIButton(position = 30, icon = VaadinIcon.EDIT, showIcon = true, caption = "",
            variants = ButtonVariant.LUMO_TERTIARY_INLINE)
    public void edit() {
        editAction.accept(contact);
    }
Bind Read-Only State

To change a component’s read-only behavior, linkki provides the annotation @BindReadOnly. This annotation can be used in combination with @Bind or UI-annotations.

The @BindReadOnly annotation is evaluated after @Bind or @UI-annotations which might already have set a read-only state.
This annotation should be used only in exceptional cases, since most of the behavior is better controlled by a PropertyBehavior.

The @BindReadOnly Annotation has only one property, ReadOnlyType. Per default, ReadOnlyType#ALWAYS is selected. Following ReadOnlyTypes are available:

Table 5. ReadOnlyType

ALWAYS

The component is always read-only (default).

DYNAMIC

The read-only behavior of the component is determined by the return value of the method is<PropertyName>ReadOnly().

DERIVED

The component is read-only if no setter method exists or the property dispatcher returns read-only for this property.

If a component is supposed to be writable even though the rest of the UI is in read-only-mode, use @BindReadOnlyBehavior with value ACTIVE, e.g. an input field to filter the content of a table.
Bind Read-Only Behavior

@BindReadOnlyBehavior changes a component’s behaviour if it is set to read-only. The read-only status is determined by the property-dispatcher-chain. There is no need to define additional methods as is usually the case with dynamic aspect definitions.

The aspect has a value of type ReadOnlyBehaviorType with the following options available:

Table 6. ReadOnlyBehaviorType

INVISIBLE

Component is invisible in read-only mode. This type is the default value.

DISABLED

Component is visible but disabled in read-only mode.

WRITABLE

Component remains writable in read-only mode.

INVISIBLE_IF_WRITABLE

Component is invisible in writable mode.

INVISIBLE and DISABLED are especially useful for buttons as buttons do not have a read-only mode. The type WRITABLE is useful for components which do not change data like an input field that is used to filter the content in the user interface.
INVISIBLE_IF_WRITABLE can be used for buttons which should only be visible in read-only mode.
INVISIBLE, DISABLED, INVISIBLE_IF_WRITABLE are only supported for buttons yet. WRITABLE can only be used on UI elements which implement HasValue.
Bind Visible

@BindVisible changes a component’s visibility from the PMO by invoking a method named is[PropertyName]Visible(). When annotating an entire PMO the method is isVisible().

The aspect has a default value of type VisibiltyType with the option DYNAMIC. Using this annotation, it is no longer necessary to specify the visible property with VisibleType.DYNAMIC in components, e.g. @UILabel.

Updates of child bindings are skipped if a PMO is invisible. Due to an initial update upon creation, null-/Exception-handling might however still be necessary. To address this, consider:

  1. Creating the PMO with a dummy model object to facilitate the initial update, then switch to the correct model object and use BindingContext.uiUpdated() for subsequent updates.

  2. Retain null checks in getter methods without direct model binding to ensure stability during the initial update.

Style Names

Vaadin components get rendered as HTML and styled via CSS. Using the @BindStyleNames annotation custom style names can be bound to a component in addition to those provided by Vaadin.

A single style name can be provided as the annotation’s value (@BindStyleNames("foo")) as well as an array of multiple style names (@BindStyleNames({"bar", "baz"})).

The value can also be omitted, leading to dynamic resolution via a get<Property>StyleNames() method that may return a String or any Collection<String>.

This aspect is an inherited aspect if it is declared on a class. Style names defined in the inheritance hierarchy are all added to the component.
Combo Box Item Style

With the @BindComboBoxItemStyle annotation it is possible to style the items in a combo box popup menu. The annotation can only be used in conjunction with the annotation @UIComboBox.

The CSS style names can be specified directly in the annotation and then apply to all items.

Alternatively, the annotation can be specified without style names, in which case a method named get<Property>ItemStyle() is called with return type Function<TYPE, String>. The type TYPE must correspond to the type of the items in the combo box. The function is called for each item in the combo box. This makes it possible to define different styles for individual items.

@BindComboBoxDynamicCaption

By default, the caption of an item is only updated if the item itself changes. If the caption of an item can change without the item itself being changed, the combo box should be additionally be annotated with @BindComboBoxDynamicCaption. This annotation makes sure that all items are updated including their caption upon updates.

The update of all items can be costly, especially if the combo box contains a large number of items.
Caption

For sections and applicable fields(e.g. UICheckbox, UIButton), the caption can be set with the separate @BindCaption annotation.

The @BindCaption annotation has two properties:

Value

This is the text displayed on CaptionType.STATIC. Its default value is an empty string ("").

CaptionType

The following configuration options are available for CaptionType:

Table 7. CaptionType

AUTO

Reacts as DYNAMIC if value is empty or STATIC if it is not empty (default)

STATIC

The caption is read from the attribute value

DYNAMIC

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

NONE

Explicitly set the caption to null

Icon

UI elements such as @UIButton or @UILink can have an icon that is shown with the component’s caption. This can be set with the @BindIcon annotation.

The @BindIcon annotation has two properties:

Value

This is the icon displayed on IconType.STATIC. Its default value is a smiley that should alarm the user in case of unintentional usage.

IconType

The following configuration options are available for IconType:

Table 8. IconType

AUTO

Reacts as STATIC if icon is specified or DYNAMIC if not (default)

STATIC

The icon is read from the attribute value

DYNAMIC

The icon is determined by the return value of the method Resource get<PropertyName>Icon(). The value is ignored.

The class VaadinIcon contains many Icons for easy use. The Vaadin documentation includes a reference page listing all available icons as well as documentation how to Icons.
Since annotations do not support a null value and VaadinIcon do not have a "NO_ICON" we cannot really specify "no icon" to automatically use a dynamic one. Hence we decided to use a quite uncommon icon (NATIVE_BUTTON) as default. If you really need exactly this icon, use iconType=STATIC explicitly.
Suffix

The annotation @BindSuffix can be used to add a suffix on UI elements.

This can be useful to display units or currencies.

The @BindSuffix annotation has two properties:

Value

This is the content of the suffix that is set at the end of a UI element.

SuffixType

The following configuration options are available for SuffixType:

Table 9. SuffixType

AUTO

The text of the suffix is read from the attribute value if it is not empty, otherwise it reacts like DYNAMIC (default)

STATIC

The text of the suffix is read from the attribute value

DYNAMIC

The text of the suffix is determined by the return value of the method String get<PropertyName>Suffix(). The value is ignored.

BindSuffix can add a suffix to UI elements which implement the HasPrefixAndSuffix interface. Common UI elements which support @BindSuffix are @UIIntegerField, @UIDoubleField, @UIDecimalField, @UITextField, @UITextArea, @UICustomField.
Placeholder

The annotation @BindPlaceholder can be used to display a placeholder on UI elements.

The @BindPlaceholder annotation has two properties:

Value

This is the content that is displayed in a UI element when it is empty.

PlaceholderType

The following configuration options are available for PlaceholderType:

Table 10. PlaceholderType

AUTO

The text of the placeholder is read from the attribute value if it is not empty, otherwise it reacts like DYNAMIC (default)

STATIC

The text of the placeholder is read from the attribute value

DYNAMIC

The text of the placeholder is determined by the return value of the method String get<PropertyName>Placeholder(). The value is ignored.

Tables

The @BindPlaceholder annotation can also be used on table PMOs providing a placeholder text that is shown in case the table has no items. Table header and footer will also be hidden.

This aspect is an inherited aspect. If a class is annotated with @BindPlaceholder, all subclasses will hide their tables using the declared annotation in their super class. It is not possible for subclasses to remove this behavior. It is however possible to set an new placeholder in a subclass.
@BindPlaceholder on table PMO
    @BindPlaceholder("No rows present.")
    @UISection(caption = "@BindPlaceholder(\"No rows present.\")")
    public static class TableWithPlaceholderPmo extends SimplePlaygroundTablePmo {
@BindPlaceholder(value = "", placeholderType = PlaceholderType.STATIC) will hide empty tables without showing any replacement text. When using @BindPlaceholder as annotation only, providing mandatory getPlaceholder()-Method and returning empty String will do the same.
AutoFocus

The annotation @BindAutoFocus can be used to set the autofocus attribute on a UI element.

This annotation should only be used on one UI element per page/dialog. The target element must be visible and editable. This annotation does not work with @UIRadioButtons.
When used on a @UIDateTimeField, the date input field will be autofocused.
@BindAutoFocus on a text field
    @BindAutoFocus
    @UITextField(position = 20, label = "autofocus")
    public String getAutoFocusedTextField() {
        return text;
    }
BindAutoFocus can set the autofocus property on UI elements which implement the HasValue and HasElement interfaces. Common UI elements which support @BindAutoFocus are @UIIntegerField, @UIDoubleField, @UIDecimalField, @UITextField, @UITextArea, @UICustomField, @UIDateField, @UIDateTimeField, @UICheckBox and @UIComboBox.
Variant names

Vaadin components get rendered as HTML and styled via CSS. The @BindVariantNames annotation can be used to add specific Vaadin variants to components implementing the HasTheme interface. It is possible to provide a single variant name (@BindVariantNames("no-border")) or an array of variant names (@BindVariantNames({"no-row-borders", "compact"})) as the annotation’s value.

@BindVariantNames on TablePmo
    @UISection(caption = "Table with 'no row border' variant")
    @BindVariantNames(value = { "no-row-borders" })
    public static class BindVariantNamesTablePmoNoBorder extends SimplePlaygroundTablePmo {
        public BindVariantNamesTablePmoNoBorder() {
            super(IntStream.range(1, 2)
                    .mapToObj(TableModelObject::new)
                    .collect(Collectors.toList()));
        }
    }
Binding validation messages

In some cases, it is not practical to use model binding in all fields of a PMO (e.g. because conversion/navigation needs to be done in getters/setters), but it should still be possible to set validation messages on a field.

This can be done with the annotation @BindMessages.

Binding labels

In some cases, the labels of UI elements as text fields for example require to be set dynamically. This can be done by using the @BindLabel annotation. One use case is to avoid unnecessary additional UI elements with static labels which have to be made visible as needed in order to provide the same functionality as dynamic labels.

This binding does not work with table headers.

The annotation has two properties:

Value

This is the label text which is displayed in case of static labels.

LabelType

The following configuration options are available for LabelType:

Table 11. LabelType

DYNAMIC

The text of the label is determined by the return value of the method String get<PropertyName>Label(). The value is ignored.

STATIC

The text of the label is read from the attribute value.

AUTO

The text of the label is read from the attribute value if it is not empty, otherwise it reacts like DYNAMIC (default).

NONE

No label text is displayed.

Some UI elements already provide a property for defining static labels. This value is overwritten by the @BindLabel annotation.
@BindLabel with a dynamic label
    @BindLabel(labelType = LabelType.DYNAMIC)
    @UIButton(position = 20)
    public void dynamicButton() {
        // button
    }

    public String getDynamicButtonLabel() {
        return dynamicLabel;
    }
@BindLabel with a static label
    @BindLabel(labelType = LabelType.STATIC, value = "Static label")
    @UIButton(position = 65)
    public void staticButton() {
        // button
    }
Binding slots

Web components are based on a layout with a fixed CSS style. The web component often defines slots where child elements could be placed. For example a layout component may define a header and a body section. The @BindSlot annotation can be used to set UI elements into these slots.

The annotation has one property:

Value

This is the name of the slot the annotated component should be set into.

The annotation can be used in combination with a newly created layout or with an existing one which already defines slots. By using @BindSlot, UI components can be set into the available slots of the used layout. For a Lit template which defines a left and a right sided slot, this looks as follows:

Example usage of @BindSlot with a slot on the left and on the right side
@UIBindSlotLayout
public class BindSlotPmo {

    private final RightSlotPmo rightSlot;

    public BindSlotPmo(RightSlotPmo rightSlot) {
        this.rightSlot = rightSlot;
    }

    @BindSlot(BindSlotLayout.SLOT_LEFT)
    @UIButton(position = 10, showIcon = true, icon = VaadinIcon.ARROW_LEFT, caption = StringUtils.EMPTY)
    public void leftButton() {
        // click
    }

    @BindSlot(BindSlotLayout.SLOT_RIGHT)
    @UINestedComponent(position = 20, width = "inherit")
    public Object rightSlot() {
        return rightSlot;
    }

}