5.8.7.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; } } }
-
Запускаем сервер приложения. Экран редактирования должен выглядеть следующим образом: