В Раздел 4.5.10.2, «Интеграция компонентов в Generic UI» были рассмотрены принципы интеграции "нативных" компонентов в универсальный UI для того, чтобы их можно было объявлять в XML-дескрипторах экранов и связывать с данными.
В предыдущем разделе мы подключили в проект сторонний компонент Stepper. Рассмотрим процесс интеграции в универсальный UI класса IntStepper
, реализующего пошаговое изменение числового значения типа int
.
-
Интерфейс компонента в модуле gui:
package com.company.myproject.gui.components; import com.haulmont.cuba.gui.components.Field; public interface IntStepper extends Field { String NAME = "intStepper"; boolean isManualInputAllowed(); void setManualInputAllowed(boolean value); boolean isMouseWheelEnabled(); void setMouseWheelEnabled(boolean value); int getStepAmount(); void setStepAmount(int amount); int getMaxValue(); void setMaxValue(int maxValue); int getMinValue(); void setMinValue(int minValue); }
В качестве базового для нашего компонента выбран интерфейс
Field
. Это позволяет осуществить связь с данными (data binding), то есть отображать и редактировать значение некоторого атрибута сущности. -
Реализация компонента в модуле web:
package com.company.myproject.web.components; import com.company.myproject.gui.components.IntStepper; import com.haulmont.cuba.web.gui.components.WebAbstractField; public class WebIntStepper extends WebAbstractField<org.vaadin.risto.stepper.IntStepper> implements IntStepper { public WebIntStepper() { component = new org.vaadin.risto.stepper.IntStepper(); } @Override public boolean isManualInputAllowed() { return component.isManualInputAllowed(); } @Override public void setManualInputAllowed(boolean value) { component.setManualInputAllowed(value); } @Override public boolean isMouseWheelEnabled() { return component.isMouseWheelEnabled(); } @Override public void setMouseWheelEnabled(boolean value) { component.setMouseWheelEnabled(value); } @Override public int getStepAmount() { return component.getStepAmount(); } @Override public void setStepAmount(int amount) { component.setStepAmount(amount); } @Override public int getMaxValue() { return component.getMaxValue(); } @Override public void setMaxValue(int maxValue) { component.setMaxValue(maxValue); } @Override public int getMinValue() { return component.getMinValue(); } @Override public void setMinValue(int minValue) { component.setMinValue(minValue); } }
В квчестве базового класса выбран
WebAbstractField
, который реализует логику интерфейсаField
по связыванию с источником данных и другие его методы. -
XML-загрузчик компонента в модуле gui:
package com.company.myproject.gui.loaders; import com.company.myproject.gui.components.IntStepper; import com.haulmont.cuba.gui.components.Component; import com.haulmont.cuba.gui.xml.layout.*; import com.haulmont.cuba.gui.xml.layout.loaders.AbstractFieldLoader; import org.dom4j.Element; public class IntStepperLoader extends AbstractFieldLoader { public IntStepperLoader(Context context, LayoutLoaderConfig config, ComponentsFactory factory) { super(context, config, factory); } @Override public Component loadComponent(ComponentsFactory factory, Element element, Component parent) { IntStepper component = (IntStepper) super.loadComponent(factory, element, parent); String manualInput = element.attributeValue("manualInput"); if (manualInput != null) { component.setManualInputAllowed(Boolean.valueOf(manualInput)); } String mouseWheel = element.attributeValue("mouseWheel"); if (mouseWheel != null) { component.setMouseWheelEnabled(Boolean.valueOf(mouseWheel)); } String stepAmount = element.attributeValue("stepAmount"); if (stepAmount != null) { component.setStepAmount(Integer.valueOf(stepAmount)); } String maxValue = element.attributeValue("maxValue"); if (maxValue != null) { component.setMaxValue(Integer.valueOf(maxValue)); } String minValue = element.attributeValue("minValue"); if (minValue != null) { component.setMinValue(Integer.valueOf(minValue)); } return component; } }
Логика загрузки базовых свойств компонента
Field
сосредоточена в классеAbstractFieldLoader
. Нам достаточно загрузить только специфические свойстваIntStepper
. -
Палитра компонентов проекта в модуле web:
package com.company.myproject.web; import com.company.myproject.gui.components.IntStepper; import com.company.myproject.gui.loaders.IntStepperLoader; import com.company.myproject.web.components.WebIntStepper; import com.haulmont.cuba.gui.ComponentPalette; import com.haulmont.cuba.gui.components.Component; import com.haulmont.cuba.gui.xml.layout.ComponentLoader; import java.util.HashMap; import java.util.Map; public class AppComponentPalette implements ComponentPalette { @Override public Map<String, Class<? extends ComponentLoader>> getLoaders() { Map<String, Class<? extends ComponentLoader>> loaders = new HashMap<>(); loaders.put(IntStepper.NAME, IntStepperLoader.class); return loaders; } @Override public Map<String, Class<? extends Component>> getComponents() { Map<String, Class<? extends Component>> components = new HashMap<>(); components.put(IntStepper.NAME, WebIntStepper.class); return components; } }
-
Регистрация палитры компонентов в классе
App
модуля web:package com.company.myproject.web; import com.haulmont.cuba.web.DefaultApp; import com.haulmont.cuba.web.gui.WebUIPaletteManager; public class App extends DefaultApp { static { WebUIPaletteManager.registerPalettes(new AppComponentPalette()); } }
-
XSD компонентов проекта в модуле gui:
<xs:schema targetNamespace="http://schemas.company.com/app/0.1/app-components.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.company.com/app/0.1/app-components.xsd" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xs:element name="intStepper"> <xs:complexType> <xs:attribute name="id" type="xs:string"/> <xs:attribute name="caption" type="xs:string"/> <xs:attribute name="width" type="xs:string"/> <xs:attribute name="height" type="xs:string"/> <xs:attribute name="datasource" type="xs:string"/> <xs:attribute name="property" type="xs:string"/> <xs:attribute name="manualInput" type="xs:boolean"/> <xs:attribute name="mouseWheel" type="xs:boolean"/> <xs:attribute name="stepAmount" type="xs:int"/> <xs:attribute name="maxValue" type="xs:int"/> <xs:attribute name="minValue" type="xs:int"/> </xs:complexType> </xs:element> </xs:schema>
-
Использование компонента в экране внутри произвольного контейнера:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/window.xsd" xmlns:app="http://schemas.company.com/app/0.1/app-components.xsd" caption="msg://editCaption" class="com.company.myproject.web.customer.CustomerEdit" datasource="customerDs" focusComponent="fieldGroup" messagesPack="com.company.myproject.web.customer"> <dsContext> <datasource id="customerDs" class="com.company.myproject.entity.Customer" view="_local"/> </dsContext> <layout expand="windowActions" spacing="true"> <app:intStepper id="stepper" datasource="customerDs" property="score" caption="Score" minValue="1" maxValue="20"/> <iframe id="windowActions" screen="editWindowActions"/> </layout> </window>
В данном примере компонент
intStepper
подсоединен к атрибутуscore
сущностиCustomer
, экземпляр которой находится в источнике данныхcustomerDs
. -
Использование компонента в кастомном поле FieldGroup:
<dsContext> <datasource id="customerDs" class="com.company.myproject.entity.Customer" view="_local"/> </dsContext> <layout expand="windowActions" spacing="true"> <fieldGroup id="fieldGroup" datasource="customerDs"> <column width="250px"> <field id="name"/> <field id="score" custom="true" caption="Score"/> </column> </fieldGroup> ...
@Inject private ComponentsFactory componentsFactory; @Inject private FieldGroup fieldGroup; @Override public void init(Map<String, Object> params) { fieldGroup.addCustomField("score", new FieldGroup.CustomFieldGenerator() { @Override public Component generateField(final Datasource datasource, final String propertyId) { IntStepper stepper = componentsFactory.createComponent(IntStepper.NAME); stepper.setDatasource(datasource, propertyId); stepper.setWidth("100%"); return stepper; } }); }