Pluggable Component Factories

The pluggable component factories mechanism extends the standard component creation procedure and allows you to create different edit fields in Form, Table and DataGrid. It means that application components or your project itself can provide custom strategies that will create non-standard components and/or support custom data types.

An entry point to the mechanism is the UiComponentsGenerator.generate(ComponentGenerationContext) method. First, it tries to find ComponentGenerationStrategy implementations. If at least one strategy exists, then it iterates over strategies according to the org.springframework.core.Ordered interface and returns the first created not null component.

ComponentGenerationStrategy implementations are used to create UI components. A project can contain any number of such strategies.

ComponentGenerationContext is a class which stores the following information that can be used when creating a component:

  • metaClass - defines the entity for which the component is created.

  • property - defines the entity attribute for which the component is created.

  • valueSource - a value source that can be used to create the component.

  • options - an options object that can be used to show options.

  • xmlDescriptor - an XML descriptor which contains additional information, in case the component is declared in an XML descriptor.

  • targetClass - a target class for which a component is created, for example, Form, Table, DataGrid.

There are two built-in component strategies:

  • DefaultComponentGenerationStrategy is used to create a component according to the given ComponentGenerationContext object. Has the order value JmixOrder.LOWEST_PLATFORM_PRECEDENCE (1000). Higher values are interpreted as lower priority, the lowest value has the highest priority, same order values will result in arbitrary sort positions for the affected objects.

  • CustomDatatypesComponentGenerationStrategy is intended for generating a default field corresponding to a datatype for which other generation strategies didn’t create a field. For instance, when a datatype is custom and doesn’t match any type for which the DefaultComponentGenerationStrategy creates fields. Has the order value Integer.MAX_VALUE (0x7fffffff).

The sample below shows how to replace the default Form field generation for a certain attribute of a specific entity.

@org.springframework.stereotype.Component("sample_SalesComponentGenerationStrategy")
public class SalesComponentGenerationStrategy implements ComponentGenerationStrategy, Ordered {

    @Inject
    private UiComponents uiComponents;

    @Inject
    private Metadata metadata;

    @Nullable
    @Override
    public Component createComponent(ComponentGenerationContext context) {
        String property = context.getProperty();
        MetaClass orderMetaClass = metadata.getClass(Order.class);

        if (orderMetaClass.equals(context.getMetaClass())
                && "date".equals(property) (1)
                && context.getClass() != null
                && Form.class.isAssignableFrom(context.getClass())) { (2)
            DatePicker<Date> datePicker = uiComponents.create(DatePicker.TYPE_DATE); (3)

            ValueSource valueSource = context.getValueSource();
            if (valueSource != null) {
                datePicker.setValueSource(valueSource); (4)
            }

            return datePicker;
        }

        return null;
    }

    @Override
    public int getOrder() {
        return 50; (5)
    }
}
1 Checks the specific field of the Order entity.
2 Checks that the component is created for the Form component.
3 Creates the specific DatePicker component.
4 Binds the component to a data source.
5 Gets the order value of this object.

The sample below shows how to define a ComponentGenerationStrategy for specific datatype.

@Order(100)
@org.springframework.stereotype.Component("sample_ColorComponentGenerationStrategy")
public class ColorComponentGenerationStrategy implements ComponentGenerationStrategy {

    @Inject
    private UiComponents uiComponents;

    @Nullable
    @Override
    public Component createComponent(ComponentGenerationContext context) {
        String property = context.getProperty();
        MetaPropertyPath mpp = context.getMetaClass().getPropertyPath(property);

        if (mpp != null) {
            Range mppRange = mpp.getRange();
            if (mppRange.isDatatype()
                    && (Datatype) mppRange.asDatatype() instanceof ColorDatatype) {
                ColorPicker colorPicker = uiComponents.create(ColorPicker.class);
                colorPicker.setDefaultCaptionEnabled(true);

                ValueSource valueSource = context.getValueSource();
                if (valueSource != null) {
                    colorPicker.setValueSource(valueSource);
                }
                return colorPicker;
            }
        }
        return null;
    }

}