3.5.13. 插件工厂

插件工厂机制扩展了标准组件的创建过程,允许在 FieldGroupTableDataGrid 中创建不同的编辑字段。这意味着应用程序组件或应用程序项目本身可以提供自定义策略,以创建非标准的组件或支持自定义数据类型。

该机制的入口点是 UiComponentsGenerator.generate(ComponentGenerationContext) 方法。其工作原理如下:

  • 尝试查找 ComponentGenerationStrategy 实现。如果找到至少一种策略,那么:

    • 根据 org.springframework.core.Ordered 接口遍历策略。

    • 返回第一个创建的非 null 组件。

ComponentGenerationStrategy 实现用于创建 UI 组件。项目可以包含任意数量的此类策略。

ComponentGenerationContext 是一个类,该类存储创建组件时可以使用的以下信息:

  • metaClass - 定义为其创建组件的实体。

  • property - 定义为其创建组件的实体属性。

  • datasource - 数据源。

  • optionsDatasource - 可用于显示选项列表的数据源。

  • valueSource - 可用于创建组件的值来源。

  • options - 可用于显示选项的选项对象。

  • xmlDescriptor - 在组件以声明的方式在 XML 描述中定义时,包含附加信息的 XML 描述。

  • componentClass - 要创建的组件的类型。例如,FieldGroupTableDataGrid

有两种内置组件策略:

  • DefaultComponentGenerationStrategy - 用于根据给定的 ComponentGenerationContext 对象创建组件。顺序值为 ComponentGenerationStrategy.LOWEST_PLATFORM_PRECEDENCE (1000)。

  • DataGridEditorComponentGenerationStrategy - 用于根据给定的 ComponentGenerationContext 对象为数据网格编辑器创建组件。顺序值为 ComponentGenerationStrategy.HIGHEST_PLATFORM_PRECEDENCE + 30 (130)。

以下示例展示如何替换为特定实体的某个属性 FieldGroup 组件的默认生成过程。

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 FieldGroup component
        if (orderMetaClass.equals(context.getMetaClass())
                && "date".equals(property)
                && context.getComponentClass() != null
                && FieldGroup.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;
    }
}

请注意,在更改返回的组件类型时,覆盖现有的生成策略可能会产生错误,因为界面控制器中的代码可能依赖于特定的组件类型。

例如,在使用上述策略的情况下,以下注入将产生异常:

@Named("fieldGroup.date")
private DateField<Date> dateField;

如果尝试打开此类界面,则会出现以下异常:

IllegalArgumentException: Can not set com.haulmont.cuba.gui.components.DateField field com.company.sales.web.order.OrderEdit.dateField to com.haulmont.cuba.web.gui.components.WebDatePicker

以下示例展示如何为特定的 datatype 定义 ComponentGenerationStrategy

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;
    }
}