3.5.13. Pluggable Component Factories
The pluggable component factories mechanism extends the standard component creation procedure and allows you to create different edit fields in FieldGroup, 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. It works as follows:
-
Tries to find
ComponentGenerationStrategy
implementations. If at least one strategy exists, then:-
Iterates over strategies according to the
org.springframework.core.Ordered
interface. -
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. -
datasource
- a datasource. -
optionsDatasource
- a datasource that can be used to show options. -
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. -
componentClass
- a component class for which a component is created, e.g.FieldGroup
,Table
,DataGrid
.
There are two built-in component strategies:
-
DefaultComponentGenerationStrategy
- used to create a component according to the givenComponentGenerationContext
object. Has the order valueComponentGenerationStrategy.LOWEST_PLATFORM_PRECEDENCE
(1000). -
DataGridEditorComponentGenerationStrategy
- used to create a component according to the givenComponentGenerationContext
object for a DataGrid Editor. Has the order valueComponentGenerationStrategy.HIGHEST_PLATFORM_PRECEDENCE + 30
(130).
The sample below shows how to replace the default FieldGroup
field generation for a certain attribute of a specific entity.
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;
}
}
Pay attention that overriding existing generation strategies may produce errors in case of changing returned component type, because some screen controllers may have code that expects a certain component type. For example, in case of using the strategy above, the following injection will produce the exception:
If you try to open such screen you will get the following exception: 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 |
The sample below shows how to define a ComponentGenerationStrategy
for a specific 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;
}
}