5.8.8.2. Подключение аддона Vaadin с интеграцией в Generic UI
В предыдущем разделе мы подключили в проект сторонний компонент Stepper. В данном разделе мы интегрируем его в универсальный пользовательский интерфейс CUBA, что даст разработчикам возможность использовать компонент декларативно в XML-дескрипторах экранов и связывать его с сущностями через источники данных.
Создадим новый проект в CUBA Studio и назовем его addon-gui-demo. В поле Project namespace введем значение agd.
Создадим модуль web-toolkit, нажав на кнопку Create web toolkit module секции Project properties навигатора Studio.
Далее нажимаем на кнопку Create new UI component. Откроется окно создания визуального компонента New UI component. В секции Component type выбираем значение Vaadin add-on.
 
  Заполним поля Add-on Maven dependency и Inherited widgetset как описано в предыдущем разделе.
Далее заполним поля в нижней секции:
-  
Integrate into Generic UI указывает на необходимости интеграции компонента в универсальный пользовательский интерфейс платформы.
 -  
Component XML element - имя элемента компонента в XML-дескрипторе экрана. Введите значение
stepper. -  
Component interface name - имя интерфейса компонента для универсального UI платформы. Введите
Stepper. -  
FQN of Vaadin component from add-on - полное имя класса компонента Vaadin из аддона. В нашем случае это
org.vaadin.risto.stepper.IntStepper. 
После нажатия кнопки OK Studio сделает следующее:
-  
Добавит аддон Vaadin в зависимости модуля web в файле
build.gradle. -  
Подключит виджетсет аддона в файле
AppWidgetSet.gwt.xmlмодуля web-toolkit. -  
Сгенерирует заготовки для следующих файлов:
-  
Stepper- интерфейс компонента в модуле gui. -  
WebStepper- реализация компонента в модуле web. -  
StepperLoader- XML-загрузчик компонента в модуле gui. -  
ui-component.xsd- описатель схемы XML для нового компонента. Если файл уже существовал на момент генерации компонента, то информация о новом компоненте будет добавлена в существующий файл. -  
cuba-ui-component.xml- файл регистрации загрузчика нового компонента в модуле web. Если файл существовал, то информация о новом компоненте будет добавлена в существующий файл. 
 -  
 
Откройте проект в IDE.
Последовательно пройдемся по сгенерированным Studio заготовкам файлов и внесем в них необходимые изменения.
-  
Перейдите к интерфейсу
Stepperв модуле gui. Замените его содержимое на следующий код:package com.company.addonguidemo.gui.components; import com.haulmont.cuba.gui.components.Field; public interface Stepper extends Field { String NAME = "stepper"; 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), то есть отображать и редактировать значение некоторого атрибута сущности. -  
Далее перейдите к классу
WebStepper- реализации компонента в модуле web. Замените содержимое класса следующим кодом:package com.company.addonguidemo.web.gui.components; import com.company.addonguidemo.gui.components.Stepper; import com.haulmont.cuba.web.gui.components.WebAbstractField; import org.vaadin.risto.stepper.IntStepper; public class WebStepper extends WebAbstractField<IntStepper> implements Stepper { public WebStepper() { this.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. -  
StepperLoaderв модуле gui загружает компонент из его представления в XML.package com.company.addonguidemo.gui.xml.layout.loaders; import com.company.addonguidemo.gui.components.Stepper; import com.haulmont.cuba.gui.xml.layout.loaders.AbstractFieldLoader; public class StepperLoader extends AbstractFieldLoader<Stepper> { @Override public void createComponent() { resultComponent = factory.createComponent(Stepper.class); loadId(resultComponent, element); } @Override public void loadComponent() { super.loadComponent(); String manualInput = element.attributeValue("manualInput"); if (manualInput != null) { resultComponent.setManualInputAllowed(Boolean.valueOf(manualInput)); } String mouseWheel = element.attributeValue("mouseWheel"); if (mouseWheel != null) { resultComponent.setMouseWheelEnabled(Boolean.valueOf(mouseWheel)); } String stepAmount = element.attributeValue("stepAmount"); if (stepAmount != null) { resultComponent.setStepAmount(Integer.valueOf(stepAmount)); } String maxValue = element.attributeValue("maxValue"); if (maxValue != null) { resultComponent.setMaxValue(Integer.valueOf(maxValue)); } String minValue = element.attributeValue("minValue"); if (minValue != null) { resultComponent.setMinValue(Integer.valueOf(minValue)); } } }Логика загрузки базовых свойств компонента
Fieldсосредоточена в классеAbstractFieldLoader. Нам достаточно загрузить только специфические свойстваStepper. -  
В файле
cuba-ui-component.xml, расположенном в корне модуля web, регистрируется новый компонент и его загрузчик. Оставляем файл без изменений.<?xml version="1.0" encoding="UTF-8" standalone="no"?> <components> <component> <name>stepper</name> <componentLoader>com.company.addonguidemo.gui.xml.layout.loaders.StepperLoader</componentLoader> <class>com.company.addonguidemo.web.gui.components.WebStepper</class> </component> </components> -  
Файл
ui-component.xsd, расположенный в корне модуля gui, это описатель XML схемы новых компонентов проекта. Добавим к элементуstepperописание его атрибутов.<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xs:schema xmlns="http://schemas.company.com/agd/0.1/ui-component.xsd" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://schemas.company.com/agd/0.1/ui-component.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="stepper"> <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> 
Далее рассмотрим, как добавить новый компонент на экран.
-  
Создадим новую сущность
Customerс двумя полями:-  
nameтипа String -  
scoreтипа Integer 
 -  
 -  
Сгенерируем для новой сущности стандартные экраны.
 -  
Далее добавим компонент
stepperна экран. Вы можете поместить его как в FieldGroup, так и в отдельный контейнер. Рассмотрим оба способа.-  
Использование компонента в экране внутри произвольного контейнера.
-  
Откройте файл
customer-edit.xml. -  
Объявите новое пространство имен
xmlns:app="http://schemas.company.com/agd/0.1/ui-component.xsd" -  
Удалите поле
scoreизfieldGroup. -  
Добавьте компонент
stepperна экран. 
В результате XML-дескриптор редактора должен выглядеть так:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/window.xsd" xmlns:app="http://schemas.company.com/agd/0.1/ui-component.xsd" caption="msg://editCaption" class="com.company.addonguidemo.gui.customer.CustomerEdit" datasource="customerDs" focusComponent="fieldGroup" messagesPack="com.company.addonguidemo.gui.customer"> <dsContext> <datasource id="customerDs" class="com.company.addonguidemo.entity.Customer" view="_local"/> </dsContext> <layout expand="windowActions" spacing="true"> <fieldGroup id="fieldGroup" datasource="customerDs"> <column width="250px"> <field id="name"/> </column> </fieldGroup> <app:stepper id="stepper" datasource="customerDs" property="score" caption="Score" minValue="1" maxValue="20"/> <frame id="windowActions" screen="editWindowActions"/> </layout> </window>В данном примере компонент
stepperподсоединен к атрибутуscoreсущностиCustomer, экземпляр которой находится в источнике данныхcustomerDs. -  
 -  
Использование компонента в поле FieldGroup:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/window.xsd" caption="msg://editCaption" class="com.company.addonguidemo.gui.customer.CustomerEdit" datasource="customerDs" focusComponent="fieldGroup" messagesPack="com.company.addonguidemo.gui.customer"> <dsContext> <datasource id="customerDs" class="com.company.addonguidemo.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"/> </column> </fieldGroup> <frame id="windowActions" screen="editWindowActions"/> </layout> </window>package com.company.addonguidemo.gui.customer; import com.company.addonguidemo.entity.Customer; import com.company.addonguidemo.gui.components.Stepper; import com.haulmont.cuba.gui.components.AbstractEditor; import com.haulmont.cuba.gui.components.FieldGroup; import com.haulmont.cuba.gui.xml.layout.ComponentsFactory; import javax.inject.Inject; import java.util.Map; public class CustomerEdit extends AbstractEditor<Customer> { @Inject private ComponentsFactory componentsFactory; @Inject private FieldGroup fieldGroup; @Override public void init(Map<String, Object> params) { fieldGroup.addCustomField("score", (datasource, propertyId) -> { Stepper stepper = componentsFactory.createComponent(Stepper.class); stepper.setDatasource(datasource, propertyId); stepper.setWidth("100%"); return stepper; }); } } 
 -  
 -  
Для адаптации внешнего вида компонента создадим в проекте расширение темы. Для этого в Studio выполним команду Create theme extension секции Project properties навигатора. В списке тем для расширения выберем
haloи нажмем кнопку Create. Затем откроем файлthemes/halo/halo-ext.scssмодуля web, и добавим в него следующий код:@import "../halo/halo"; /* Define your theme modifications inside next mixin */ @mixin halo-ext { @include halo; /* Basic styles for stepper inner text box */ .stepper input[type="text"] { @include box-defaults; @include valo-textfield-style; &:focus { @include valo-textfield-focus-style; } } } -  
Запускаем сервер приложения. Экран редактирования должен выглядеть следующим образом: