3.5.17.3.2. Подключение аддона Vaadin с интеграцией в Generic UI

В предыдущем разделе мы подключили в проект сторонний компонент Stepper. В данном разделе мы интегрируем его в универсальный пользовательский интерфейс CUBA, что даст разработчикам возможность использовать компонент декларативно в XML-дескрипторах экранов и связывать его с сущностями через Компоненты данных.

Чтобы интегрировать Stepper в Generic UI, необходимо создать следующие файлы:

  • Stepper - интерфейс компонента в подкаталоге gui модуля web.

  • WebStepper - реализация компонента в подкаталоге gui модуля web.

  • StepperLoader - XML-загрузчик компонента в подкаталоге gui модуля web.

  • ui-component.xsd - описатель схемы XML для нового компонента. Если файл уже существует, то информацию о новом компоненте следует добавить в существующий файл.

    • cuba-ui-component.xml - файл регистрации загрузчика нового компонента в модуле web. Если файл уже существует, то информацию о новом компоненте следует добавить в существующий файл.

Откройте проект в IDE.

Создадим требуемые файлы и внесем в них необходимые изменения.

  • Создайте интерфейс Stepper в подкаталоге gui модуля web. Замените его содержимое на следующий код:

    package com.company.demo.web.gui.components;
    
    import com.haulmont.cuba.gui.components.Field;
    
    // note that Stepper should extend Field
    public interface Stepper extends Field<Integer> {
    
        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 - реализации компонента в подкаталоге gui модуля web. Замените содержимое класса следующим кодом:

    package com.company.demo.web.gui.components;
    
    import com.haulmont.cuba.web.gui.components.WebV8AbstractField;
    import org.vaadin.risto.stepper.IntStepper;
    
    // note that WebStepper should extend WebV8AbstractField
    public class WebStepper extends WebV8AbstractField<IntStepper, Integer, Integer> implements Stepper {
    
        public WebStepper() {
            this.component = createComponent();
    
            attachValueChangeListener(component);
        }
    
        private IntStepper createComponent() {
            return new 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);
        }
    }

    В качестве базового класса выбран WebV8AbstractField, который реализует логику интерфейса Field.

  • StepperLoader в модуле web загружает компонент из его представления в XML.

    package com.company.demo.web.gui.xml.layout.loaders;
    
    import com.company.demo.web.gui.components.Stepper;
    import com.haulmont.cuba.gui.xml.layout.loaders.AbstractFieldLoader;
    
    public class StepperLoader extends AbstractFieldLoader<Stepper> {
        @Override
        public void createComponent() {
            resultComponent = factory.create(Stepper.class);
            loadId(resultComponent, element);
        }
    
        @Override
        public void loadComponent() {
            super.loadComponent();
    
            String manualInput = element.attributeValue("manualInput");
            if (manualInput != null) {
                resultComponent.setManualInputAllowed(Boolean.parseBoolean(manualInput));
            }
            String mouseWheel = element.attributeValue("mouseWheel");
            if (mouseWheel != null) {
                resultComponent.setMouseWheelEnabled(Boolean.parseBoolean(mouseWheel));
            }
            String stepAmount = element.attributeValue("stepAmount");
            if (stepAmount != null) {
                resultComponent.setStepAmount(Integer.parseInt(stepAmount));
            }
            String maxValue = element.attributeValue("maxValue");
            if (maxValue != null) {
                resultComponent.setMaxValue(Integer.parseInt(maxValue));
            }
            String minValue = element.attributeValue("minValue");
            if (minValue != null) {
                resultComponent.setMinValue(Integer.parseInt(minValue));
            }
        }
    }

    Логика загрузки базовых свойств компонента Field сосредоточена в классе AbstractFieldLoader. Нам достаточно загрузить только специфические свойства Stepper.

  • В файле cuba-ui-component.xml, расположенном в корне модуля web, регистрируется новый компонент и его загрузчик. Замените содержимое файла следующим кодом:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <components xmlns="http://schemas.haulmont.com/cuba/components.xsd">
        <component>
            <name>stepper</name>
            <componentLoader>com.company.demo.web.gui.xml.layout.loaders.StepperLoader</componentLoader>
            <class>com.company.demo.web.gui.components.WebStepper</class>
        </component>
    </components>
  • Файл ui-component.xsd, расположенный в корне модуля web, это описатель XML схемы новых компонентов проекта. Добавьте элемент stepper и описание его атрибутов.

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <xs:schema xmlns="http://schemas.company.com/agd/0.1/ui-component.xsd"
               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="height" type="xs:string"/>
                <xs:attribute name="width" type="xs:string"/>
    
                <xs:attribute name="dataContainer" 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>

Далее рассмотрим, как добавить новый компонент на экран.

  • Либо удалите изменения, сделанные в предыдущем разделе, либо сгенерируйте для сущности стандартные экраны.

  • Далее добавим компонент stepper на экран. Вы можете добавить его как декларативно, так и программно. Рассмотрим оба способа.

    1. Декларативное использование компонента в XML-декскрипторе.

      • Откройте файл customer-edit.xml.

      • Объявите новое пространство имен xmlns:app="http://schemas.company.com/agd/0.1/ui-component.xsd"

      • Удалите поле score из form.

      • Добавьте компонент stepper на экран.

      В результате XML-дескриптор редактора должен выглядеть так:

      <?xml version="1.0" encoding="UTF-8" standalone="no"?>
      <window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
              xmlns:app="http://schemas.company.com/agd/0.1/ui-component.xsd"
              caption="msg://editorCaption"
              focusComponent="form"
              messagesPack="com.company.demo.web.customer">
          <data>
              <instance id="customerDc"
                        class="com.company.demo.entity.Customer"
                        view="_local">
                  <loader/>
              </instance>
          </data>
          <dialogMode height="600"
                      width="800"/>
          <layout expand="editActions" spacing="true">
              <form id="form" dataContainer="customerDc">
                  <column width="250px">
                      <textField id="nameField" property="name"/>
                      <app:stepper id="stepper"
                                   dataContainer="customerDc" property="score"
                                   minValue="0" maxValue="20"/>
                  </column>
              </form>
              <hbox id="editActions" spacing="true">
                  <button action="windowCommitAndClose"/>
                  <button action="windowClose"/>
              </hbox>
          </layout>
      </window>

      В данном примере компонент stepper подсоединен к атрибуту score сущности Customer, экземпляр которой находится в источнике данных customerDc.

    2. Программное создание в контроллере Java.

      <?xml version="1.0" encoding="UTF-8" standalone="no"?>
      <window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
              caption="msg://editorCaption"
              focusComponent="form"
              messagesPack="com.company.demo.web.customer">
          <data>
              <instance id="customerDc"
                        class="com.company.demo.entity.Customer"
                        view="_local">
                  <loader/>
              </instance>
          </data>
          <dialogMode height="600"
                      width="800"/>
          <layout expand="editActions" spacing="true">
              <form id="form" dataContainer="customerDc">
                  <column width="250px">
                      <textField id="nameField" property="name"/>
                  </column>
              </form>
              <hbox id="editActions" spacing="true">
                  <button action="windowCommitAndClose"/>
                  <button action="windowClose"/>
              </hbox>
          </layout>
      </window>
      package com.company.demo.web.customer;
      
      import com.company.demo.entity.Customer;
      import com.company.demo.web.gui.components.Stepper;
      import com.haulmont.cuba.gui.UiComponents;
      import com.haulmont.cuba.gui.components.Form;
      import com.haulmont.cuba.gui.components.data.value.ContainerValueSource;
      import com.haulmont.cuba.gui.model.InstanceContainer;
      import com.haulmont.cuba.gui.screen.*;
      
      import javax.inject.Inject;
      
      @UiController("demo_Customer.edit")
      @UiDescriptor("customer-edit.xml")
      @EditedEntityContainer("customerDc")
      @LoadDataBeforeShow
      public class CustomerEdit extends StandardEditor<Customer> {
          @Inject
          private Form form;
          @Inject
          private InstanceContainer<Customer> customerDc;
          @Inject
          private UiComponents uiComponents;
      
          @Subscribe
          protected void onInit(InitEvent event) {
              Stepper stepper = uiComponents.create(Stepper.NAME);
              stepper.setValueSource(new ContainerValueSource<>(customerDc, "score"));
              stepper.setCaption("Score");
              stepper.setWidthFull();
              stepper.setMinValue(0);
              stepper.setMaxValue(20);
      
              form.add(stepper);
          }
      
          @Subscribe
          protected void onInitEntity(InitEntityEvent<Customer> event) {
              event.getEntity().setScore(0);
          }
      }
  • Запускаем сервер приложения. Экран редактирования должен выглядеть следующим образом:

customer edit result