3.5.15. Подключаемые фабрики компонентов

Механизм подключаемых фабрик компонентов расширяет процедуру генерации компонентов и позволяет создавать различные поля редактирования в Form, Table и DataGrid. Это означает, что компоненты приложения или сам ваш проект могут предоставлять собственные стратегии, которые будут создавать нестандартные компоненты и/или поддерживать кастомные типы данных.

Точкой входа в данный механизм является метод UiComponentsGenerator.generate(ComponentGenerationContext). Он работает следующим образом:

  • Пытается найти все реализации интерфейса ComponentGenerationStrategy. Если как минимум одна реализация существует:

    • Обходит все реализации в соответствии с интерфейсом org.springframework.core.Ordered.

    • Возвращается первый созданный не нулевой компонент.

Реализации интерфейса ComponentGenerationStrategy используются при создании UI компонентов. Проект может содержать любое количество таких стратегий.

ComponentGenerationContext - класс, содержащий следующую информацию, которая может быть использована при создании компонента:

  • metaClass - задает сущность, для которой создается компонент.

  • property - задает атрибут сущности, для которой создается компонент.

  • datasource - источник данных, который может быть связан с компонентом.

  • optionsDatasource - источник данных, который может быть связан с компонентом для показа опций.

  • valueSource - источник данных, который может быть связан с компонентом.

  • options - источник данных, который может быть связан с компонентом для показа опций.

  • xmlDescriptor - XML дескриптор с дополнительной информацией, в случае, если компонент описан в XML дескрипторе.

  • componentClass - класс компонента для которого должен быть создан компонент (например, Table, Form, DataGrid).

В платформе существуют две стандартных реализации ComponentGenerationStrategy:

  • DefaultComponentGenerationStrategy - используется для создания компонентов в соответствии с переданным ComponentGenerationContext. Имеет значение order равное ComponentGenerationStrategy.LOWEST_PLATFORM_PRECEDENCE (1000).

  • DataGridEditorComponentGenerationStrategy - используется для создания компонентов для DataGrid Editor в соответствии с переданным ComponentGenerationContext. Имеет значение order равное ComponentGenerationStrategy.HIGHEST_PLATFORM_PRECEDENCE + 30 (130).

Пример ниже показывает, как заменить стандартную генерацию поля в компоненте Form для определенного атрибута некоторой сущности.

import com.company.sales.entity.Order;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.gui.UiComponents;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.data.ValueSource;
import org.springframework.core.Ordered;

import javax.annotation.Nullable;
import javax.inject.Inject;
import java.sql.Date;

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

    public static final String NAME = "sales_SalesComponentGenerationStrategy";

    @Inject
    private UiComponents uiComponents;

    @Inject
    private Metadata metadata;

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

        // Check the specific field of the Order entity
        // and that the component is created for the Form component
        if (orderMetaClass.equals(context.getMetaClass())
                && "date".equals(property)
                && context.getComponentClass() != null
                && Form.class.isAssignableFrom(context.getComponentClass())) {
            DatePicker<Date> datePicker = uiComponents.create(DatePicker.TYPE_DATE);

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

            return datePicker;
        }

        return null;
    }

    @Override
    public int getOrder() {
        return 50;
    }
}

Пример ниже показывает, как определить ComponentGenerationStrategy для специализированного datatype.

import com.company.colordatatype.datatypes.ColorDatatype;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.chile.core.model.Range;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.gui.UiComponents;
import com.haulmont.cuba.gui.components.ColorPicker;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.ComponentGenerationContext;
import com.haulmont.cuba.gui.components.ComponentGenerationStrategy;
import com.haulmont.cuba.gui.components.data.ValueSource;
import org.springframework.core.annotation.Order;

import javax.annotation.Nullable;
import javax.inject.Inject;

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

    public static final String NAME = "colordatatype_ColorComponentGenerationStrategy";

    @Inject
    private UiComponents uiComponents;

    @Nullable
    @Override
    public Component createComponent(ComponentGenerationContext context) {
        String property = context.getProperty();
        MetaPropertyPath mpp = resolveMetaPropertyPath(context.getMetaClass(), 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) {
                    //noinspection unchecked
                    colorPicker.setValueSource(valueSource);
                }

                return colorPicker;
            }
        }

        return null;
    }

    protected MetaPropertyPath resolveMetaPropertyPath(MetaClass metaClass, String property) {
        MetaPropertyPath mpp = metaClass.getPropertyPath(property);

        if (mpp == null && DynamicAttributesUtils.isDynamicAttribute(property)) {
            mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, property);
        }

        return mpp;
    }
}