Release Notes

Version 1.4.0

New features and improvements

New aspect annotation: @BindVisible

For the annotation @BindVisible, the VisibleType is set to DYNAMIC, determining the visible state from the PMO by invoking a method named is[PropertyName]Visible(). Using this annotation, it is no longer necessary to specify the visible property with VisibleType.DYNAMIC in components, e.g. @UILabel. The @BindVisible can be used on any PMO, including any PMO classes that implement the ContainerPmo interface (i.e. TableSections). Using @BindVisible will overrule the existing visible property.

New BoundPropertyCreator: ModelBindingBoundPropertyCreator

The annotation @LinkkiBoundProperty now has a default value of the type ModelBindingBoundPropertyCreator. Using this BoundPropertyCreator it is very simple to create a new @UI-Field Annotation with model binding support. For more information read the corresponding documentation about BoundProperty.

Concept of BindingDefinition is deprecated

The concept of BindingDefinition is deprecated as it has already been replaced by new machenisms described in Creating a custom UI element. Therefore, the following classes and interfaces are deprecated and should not be used anymore.

  • org.linkki.core.binding.descriptor.bindingdefinition.BindingDefinition and all extending classes

  • org.linkki.core.binding.descriptor.bindingdefinition.annotation.LinkkiBindingDefinition

  • org.linkki.core.uicreation.BindingDefinitionComponentDefinition

  • org.linkki.core.ui.element.annotation.FieldAspectDefinitionCreator

Migration of custom annotations based on BindingDefinition

At the end of the migration, your custom UI annotation should not use LinkkiBindingDefinition anymore. In addition, all of the logic in the impelementation of BindingDefinition should have been moved to other classes so that the class can be removed.

The BindingDefinition interface consists of the following methods whose content have to be moved to new classes:

Component creation method Object newComponent()

This method defines how a UI component should be created. To migrate its content:

  1. Create a new static class in the annotation class that implements the interface ComponentDefinitionCreator.

    • Use your annotation type as generic type.

    • Implement the create method. You can use a lambda directly and then reuse the implemention of newComponent() of your BindingDefinition.

  2. In the annotation class, replace BindingDefinitonComponentDefinition.Creator.Class with the new implementation of ComponentDefinitionCreator.

Table 1. Example
Before After
...
@LinkkiComponent(BindingDefinitionComponentDefinition.Creator.class) //2
public @interface UIFancy {
  ...
}
...
@LinkkiComponent(FancyComponentCreator.class) //2
public @interface UIFancy {
  ...

  static class FancyComponentCreator implements ComponentDefinitionCreator<UIFancy> { //1

    public LinkkiComponentDefinition create(UIFancy annotation, AnnotatedElement annotatedElement) {
      return pmo -> {
        // Creates new component from annotation
      };
    }
  }
}
public class FancyBindingDefinition implements BindingDefinition {

  private UIFancy annotation;

  public Object newComponent() {
    // Creates new component from annotation
  }

    ...
}
Model binding methods String modelObject() and String modelAttribute()

These methods define how the model object and model attribute names should be derived from the annotation. To support model binding without BindingDefinition:

  1. In the annotation class, remove the argument BindingDefinitionBoundPropertyCreator

  2. Annotate the annotation attribute that defines the model object name with LinkkiBoundProperty.ModelObject

  3. Annotate the annotation attribute that defines the model attribute name with LinkkiBoundProperty.ModelAttribute

Table 2. Example
Before After
...
@LinkkiBoundProperty(BindingDefinitionBoundPropertyCreator.class) //1
public @interface UIFancy {

  String modelObject() ... ;

  String modelAttribute() ... ;

  ...
}
...
@LinkkiBoundProperty //1
public @interface UIFancy {

  @LinkkiBoundProperty.ModelObject //2
  String modelObject() ... ;

  @LinkkiBoundProperty.ModelAttribute //3
  String modelAttribute() ... ;

  ...
}

If your annotation doesn’t support model binding, you should use SimpleMemberNameBoundPropertyCreator instead.

Aspect methods String label(), EnabledType enabled(), VisibleType visible(), RequiredType required

These methods define values that are necessary for the aspects. The aspects that were supported by default are:

  • label

  • enabled

  • visible

  • required

  • value

  • derived read-only (readonly if setter method is missing)

To support these aspects without BindingDefinition:

  1. Create a new implementation of ApsectDefinitionCreator

    • Use the annotation type as generic type

    • In the create method, create a new CompositeAspectDefinition that contains the apsects above (see example below).

  2. Replace FieldAspectDefinitionCreator with the new custom AspectDefinitionCreator

    Table 3. Example
    Before After
    ...
    @LinkkiAspect(FieldAspectDefinitionCreator.class) //2
    public @interface UIFancy {
    
      String label() ... ;
    
      EnabledType enabled() ... ;
    
      VisibleType visible() ... ;
    
      RequiredType required() ... ;
    
      ...
    }
    ...
    @LinkkiAspect(FancyAspectDefinitionCreator.class) //2
    public @interface UIFancy {
      ...
    
      static class FancyAspectDefinitionCreator implements AspectDefinitionCreator<UIFancy> { //1
        @Override
        public LinkkiAspectDefinition create(UIFancy annotation) {
          EnabledAspectDefinition enabledAspectDefinition = new EnabledAspectDefinition(annotation.enabled());
          RequiredAspectDefinition requiredAspectDefinition = new RequiredAspectDefinition(
                        annotation.required(),
                        enabledAspectDefinition);
    
          return new CompositeAspectDefinition(new LabelAspectDefinition(
                        annotation.label()),
                        enabledAspectDefinition,
                        requiredAspectDefinition,
                        new VisibleAspectDefinition(annotation.visible()),
                        new DerivedReadOnlyAspectDefinition());
      }
    }
Faktor-IPS Property Dispatcher uses the value set information to set field properties in the UI

The IpsPropertyDispatcher can now derive the required, visible and enabled state of a field based on the value set of its model attribute. This functionality is described in the section about IpsPropertyDispatcher. If the IpsPropertyDispatcher is already in use, this new feature will automatically come into effect with the new version.

Update all binding contexts in BindingManager

BindingManager now has a new method updateAll which updates all BindingContexts that are managed by the BindingManager. This is a costly operation that should be used with caution.

Dependencies updated

Dependencies used by linkki have been updated.

Different date format for English locale

The short date format (1/1/21) in DateFormats has been replaced with a date format displaying the full year and leading zeroes (01/01/2021).

linkki for Vaadin 14

linkki now includes modules using Vaadin version 14 for a first developer preview.

Bugfixes

  • Fixed typo in ReadOnlyBehaviorType#INVISIBLE

  • Fixed log warnings due to depreacted method call in SidebarLayout. SidebarLayout.SelectionListener is now serializable, consider adding a serialVersionUID.

  • Fixed tooltip on labels not showing HTML content

  • Fixed an error when entering a year with five or more digits

  • Fixed UIDateField always showing english error message