3.5.13. Подключаемые фабрики компонентов
Механизм подключаемых фабрик компонентов расширяет процедуру генерации компонентов и позволяет создавать различные поля редактирования в FieldGroup, Table и DataGrid. Это означает, что компоненты приложения или сам ваш проект могут предоставлять собственные стратегии, которые будут создавать нестандартные компоненты и/или поддерживать кастомные типы данных.
Точкой входа в данный механизм является метод UiComponentsGenerator.generate(ComponentGenerationContext)
. Он работает следующим образом:
-
Пытается найти все реализации интерфейса
ComponentGenerationStrategy
. Если как минимум одна реализация существует:-
Обходит все реализации в соответствии с интерфейсом
org.springframework.core.Ordered
. -
Возвращается первый созданный не нулевой компонент.
-
Реализации интерфейса ComponentGenerationStrategy
используются при создании UI компонентов. Проект может содержать любое количество таких стратегий.
ComponentGenerationContext
- класс, содержащий следующую информацию, которая может быть использована при создании компонента:
-
metaClass
- задает сущность, для которой создается компонент. -
property
- задает атрибут сущности, для которой создается компонент. -
datasource
- источник данных, который может быть связан с компонентом. -
optionsDatasource
- источник данных, который может быть связан с компонентом для показа опций. -
valueSource
- источник данных, который может быть связан с компонентом. -
options
- источник данных, который может быть связан с компонентом для показа опций. -
xmlDescriptor
- XML дескриптор с дополнительной информацией, в случае, если компонент описан в XML дескрипторе. -
componentClass
- класс компонента для которого должен быть создан компонент (например,Table
,FieldGroup
,DataGrid
).
В платформе существуют две стандартных реализации ComponentGenerationStrategy
:
-
DefaultComponentGenerationStrategy
- используется для создания компонентов в соответствии с переданнымComponentGenerationContext
. Имеет значение order равноеComponentGenerationStrategy.LOWEST_PLATFORM_PRECEDENCE
(1000). -
DataGridEditorComponentGenerationStrategy
- используется для создания компонентов для DataGrid Editor в соответствии с переданнымComponentGenerationContext
. Имеет значение order равное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;
}
}
Обратите внимание, что переопределение существующих стратегий генерации компонентов может приводить к ошибкам в случае изменения типа возвращаемого компонента, поскольку некоторые контроллеры экрана могут иметь код, ожидающий определенный тип компонента. Например, в случае использования вышеприведенной стратегии, следующая инжекция приведет к исключению:
Если вы попытаетесь открыть такой экран, то получите следующее исключение: 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 |
Пример ниже показывает, как определить 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;
}
}