5.8.6.1. Пример использования стороннего компонента Vaadin

Способ подключения сторонних компонентов Vaadin описан в Раздел 4.5.10.1, «Использование сторонних компонентов Vaadin».

Рассмотрим пример использования компонента Stepper, доступного по адресу http://vaadin.com/addon/stepper. Данный компонент позволяет пошагово изменять значение текстового поля с помощью клавиатуры, колесика мыши и встроенных кнопок вверх/вниз.

  • Предположим, что в проекте имеется сущность Customer со строковым атрибутом name. В модуле web создан экран редактирования customer-edit.xml со следующей компоновкой:

    <layout expand="windowActions"
            spacing="true">
        <fieldGroup id="fieldGroup"
                    datasource="customerDs">
            <column width="250px">
                <field id="name"/>
            </column>
        </fieldGroup>
        <iframe id="windowActions"
                screen="editWindowActions"/>
    </layout>

    Нам необходимо добавить атрибут score типа Integer, и обеспечить его пошаговое редактирование в данном экране.

  • В CUBA Studio добавляем атрибут score сущности Customer:

    @Column(name = "SCORE")
    protected Integer score;
    
    public void setScore(Integer score) {
        this.score = score;
    }
    public Integer getScore() {
        return score;
    }

    Генерируем скрипты обновления БД и запускаем обновление.

  • Выполняем команду Create web toolkit module секции Project properties навигатора Studio.

  • В build.gradle проекта добавляем зависимость модуля web от add-on, содержащего компонент:

    configure(webModule) {
        ...
        dependencies {
            ...
            compile("org.vaadin.addons:stepper:2.1.2")
        }
  • Пересоздаем проектные файлы IDE: меню Create or update IDEA project files.

  • В файл AppWidgetSet.gwt.xml модуля web-toolkit проекта подключаем набор виджетов add-on после набора виджетов платформы:

    <module>
        <inherits name="com.haulmont.cuba.web.toolkit.ui.WidgetSet" />
        
        <inherits name="org.vaadin.risto.stepper.widgetset.StepperWidgetset" />
    
        <set-property name="user.agent" value="safari" />

    Для более быстрой сборки виджетов на время разработки можно установить свойство user.agent. В данном примере набор виджетов будет собираться только для браузеров, основанных на WebKit: Chrome, Safari, и т.д.

  • В XML-дескрипторе экрана редактирования customer-edit.xml добавляем кастомное поле score в компонент fieldGroup:

    <fieldGroup id="fieldGroup"
                datasource="customerDs">
        <column width="250px">
            <field id="name"/>
            <field id="score" custom="true" caption="Score"/>
        </column>
    </fieldGroup>
  • В контроллере экрана редактирования CustomerEdit добавляем следующий код:

    public class CustomerEdit extends AbstractEditor<Customer> {
    
        @Inject
        private ComponentsFactory componentsFactory;
        @Inject
        private FieldGroup fieldGroup;
    
        private IntStepper stepper = new IntStepper();
    
        @Override
        public void init(Map<String, Object> params) {
            fieldGroup.addCustomField("score", new FieldGroup.CustomFieldGenerator() {
                @Override
                public Component generateField(final Datasource datasource, final String propertyId) {
                    Component box = componentsFactory.createComponent(BoxLayout.VBOX);
                    com.vaadin.ui.Layout layout = WebComponentsHelper.unwrap(box);
                    layout.addComponent(stepper);
                    stepper.setSizeFull();
    
                    stepper.addValueChangeListener(new Property.ValueChangeListener() {
                        @Override
                        public void valueChange(Property.ValueChangeEvent event) {
                            datasource.getItem().setValue(propertyId, event.getProperty().getValue());
                        }
                    });
    
                    return box;
                }
            });
        }
    
        @Override
        protected void postInit() {
            stepper.setValue(getItem().getScore());
        }
    }

    Здесь в поле stepper создается экземпляр компонента, подключенного из add-on. В методе init() производится инициализация кастомного поля score. Через ComponentsFactory создается экземпляр BoxLayout, затем из него с помощью WebComponentsHelper извлекается ссылка на Vaadin-контейнер, и в этот контейнер добавляется наш новый компонент. BoxLayout возвращается для отображения в кастомном поле.

    Для связи компонента с данными во-первых, в методе postInit() ему устанавливается текущее значение из редактируемого Customer, а во-вторых, добавляется слушатель на изменение значения, который обновляет соответствующий атрибут сущности при изменении значения пользователем.

  • Новый компонент можно использовать и вне FieldGroup в произвольном месте экрана. Для этого в XML-дескрипторе объявим контейнер:

    <hbox id="scoreBox"
          spacing="true">
        <label value="Score"/>
    </hbox>

    В контроллере инжектируем контейнер, извлекаем ссылку на Vaadin-контейнер и добавляем в него компонент:

    public class CustomerEdit extends AbstractEditor<Customer> {
    
        @Inject
        private BoxLayout scoreBox;
    
        private IntStepper stepper = new IntStepper();
    
        @Override
        public void init(Map<String, Object> params) {
            com.vaadin.ui.Layout box = WebComponentsHelper.unwrap(scoreBox);
            box.addComponent(stepper);
    
            stepper.addValueChangeListener(new Property.ValueChangeListener() {
                @Override
                public void valueChange(Property.ValueChangeEvent event) {
                    getItem().setValue("score", event.getProperty().getValue());
                }
            });
        }
    
        @Override
        protected void postInit() {
            stepper.setValue(getItem().getScore());
        }
    }

    Связь с данными выполняется здесь аналогично примеру с FieldGroup.

  • Для адаптации внешнего вида компонента создадим в проекте расширение темы. Для этого в Studio выполним команду Create theme extension секции Project properties навигатора. Затем откроем файл themes/havana/havana-ext.scss модуля web, и добавим в него следующий код:

    @import "../havana/havana"; 
     
    @mixin havana-ext { 
      @include havana; 
     
      /* Basic styles for stepper */
      .v-stepper { 
        /* Use box-sizing: border-box; for all browsers */
        @include box-defaults; 
     
        height: 25px; 
        border: 0; 
     
        /* Use theme fonts */
        font-family: $theme_fonts; 
      } 
     
      /* Basic styles for inner text box */
      .v-stepper input[type="text"] { 
        /* Use box-sizing: border-box; for all browsers */
        @include box-defaults; 
     
        height: 25px; 
        padding: 1px; 
        outline: 0; 
        margin: 0; 
     
        /* Use border color from theme */
        border: 1px solid $theme_fieldBorderColor; 
      } 
     
      /* Focused styles */
      .v-stepper.v-stepper input[type="text"]:focus { 
        /* Use focused border color from theme */
        border-color: $theme_fieldFocusedBorderColor; 
        /* hide default focus outline */
        outline: 0; 
      } 
     
      /* Readonly styles */
      .v-readonly.v-stepper input[type="text"], 
      .v-readonly.v-stepper input[type="text"]:focus { 
        /* Use readonly border color from theme */
        border-color: $theme_fieldReadonlyBorderColor; 
      } 
    }