4.5.6.3. Подключение JavaScript библиотеки

В данном примере мы подключим компонент Slider из библиотеки jQuery UI. Слайдер будет иметь два ползунка, определяющих диапазон значений.

Создайте новый проект в CUBA Studio и назовите его jscomponent.

Далее нажмите на кнопку New UI component. Откроется окно создания визуального компонента UI component generation. В секции Component type выберите значение JavaScript component.

studio js component wizard

В поле Vaadin component class name диалога генерации компонента введите значение SliderServerComponent.

Уберите флажок Integrate into Generic UI. Процесс интеграции компонента в универсальный интерфейс аналогичен описанному в разделе Подключение аддона Vaadin с интеграцией в Generic UI, поэтому рассматривать его здесь мы не будем.

После нажатия кнопки OK Studio сгенерирует файлы:

  • SliderServerComponent - интегрированный с JavaScript компонент Vaadin.

  • SliderState - класс состояния компонента Vaadin.

  • slider-connector.js - JavaScript коннектор для компонента Vaadin

Последовательно пройдемся по сгенерированным заготовкам файлов и внесем в них необходимые изменения.

  • Класс состояния SliderState определяет, какие данные будут пересылаться между сервером и клиентом. В нашем случае мы будем пересылать информацию о минимальном и максимальном возможном значении и выбранных значениях слайдера.

    package com.company.jscomponent.web.toolkit.ui.slider;
    
    import com.vaadin.shared.ui.JavaScriptComponentState;
    
    public class SliderState extends JavaScriptComponentState {
        public double[] values;
        public double minValue;
        public double maxValue;
    }
  • Серверный компонент Vaadin SliderServerComponent.

    package com.company.jscomponent.web.toolkit.ui.slider;
    
    import com.vaadin.annotations.StyleSheet;
    import com.vaadin.ui.AbstractJavaScriptComponent;
    import com.vaadin.annotations.JavaScript;
    import elemental.json.JsonArray;
    
    @JavaScript({"slider-connector.js", "jquery-ui.js"})
    @StyleSheet({"jquery-ui.css"})
    public class SliderServerComponent extends AbstractJavaScriptComponent {
    
        public interface ValueChangeListener {
            void valueChanged(double[] newValue);
        }
    
        private ValueChangeListener listener;
    
        public SliderServerComponent() {
            addFunction("valueChanged", arguments -> {
                JsonArray array = arguments.getArray(0);
                double[] values = new double[2];
                values[0] = array.getNumber(0);
                values[1] = array.getNumber(1);
    
                getState(false).values = values;
    
                listener.valueChanged(values);
            });
        }
    
        public void setValue(double[] value) {
            getState().values = value;
        }
    
        public double[] getValue() {
            return getState().values;
        }
    
        public double getMinValue() {
            return getState().minValue;
        }
    
        public void setMinValue(double minValue) {
            getState().minValue = minValue;
        }
    
        public double getMaxValue() {
            return getState().maxValue;
        }
    
        public void setMaxValue(double maxValue) {
            getState().maxValue = maxValue;
        }
    
        @Override
        protected SliderState getState() {
            return (SliderState) super.getState();
        }
    
        @Override
        public SliderState getState(boolean markAsDirty) {
            return (SliderState) super.getState(markAsDirty);
        }
    
        public ValueChangeListener getListener() {
            return listener;
        }
    
        public void setListener(ValueChangeListener listener) {
            this.listener = listener;
        }
    }

    Серверный компонент Vaadin определяет набор геттеров и сеттеров для работы с состоянием слайдера, а также интерфейс слушателя изменения значений. Класс компонента должен быть унаследован от AbstractJavaScriptComponent.

    Метод addFunction() в конструкторе класса объявляет обработчик RPC-вызова функции valueChanged() с клиента.

    Аннотации @JavaScript и @StyleSheet указывают на файлы, которые должны быть загружены на веб-страницу. В нашем примере это JavaScript файлы библиотеки jQuery UI и коннектора, а также файл со стилями для jQuery UI. Расположим их в одном Java-пакете с серверным компонентом.

Скачайте архив jQuery UI с сайта http://jqueryui.com/download и поместите файлы jquery-ui.js и jquery-ui.css в пакет с классом SliderServerComponent. На странице скачивания jQuery UI у вас будет возможность выбрать компоненты, которые будут помещены в архив. Для нашего примера достаточно выделить пункт Slider группы Widgets.

js project structure
  • JavaScript коннектор slider-connector.js.

    com_company_jscomponent_web_toolkit_ui_slider_SliderServerComponent = function() {
        var connector = this;
        var element = connector.getElement();
        $(element).html("<div/>");
        $(element).css("padding", "5px 10px");
    
        var slider = $("div", element).slider({
            range: true,
            slide: function(event, ui) {
                connector.valueChanged(ui.values);
            }
        });
    
        connector.onStateChange = function() {
            var state = connector.getState();
            slider.slider("values", state.values);
            slider.slider("option", "min", state.minValue);
            slider.slider("option", "max", state.maxValue);
            $(element).width(state.width);
        }
    }

    Коннектор представляет собой функцию, которая при загрузке веб-страницы проинициализирует JavaScript компонент. Имя функции должно соответствовать полному имени класса серверного компонента, где точки в имени пакета заменены на символ подчеркивания.

    Vaadin добавляет ряд полезных методов в функцию коннектора. Например, this.getElement() возвращает HTML DOM элемент компонента, this.getState() возвращает объект-состояние.

    В нашем примере коннектор делает следующее:

    • Инициализирует компонент slider из библиотеки jQuery UI. При изменении положения одного из ползунков будет вызвана функция slide(). Мы видим, что она в свою очередь вызывает метод valueChanged() коннектора. valueChanged() - это метод, который мы объявили на стороне сервера в классе SliderServerComponent.

    • Объявляет функцию onStateChange(). Она будет вызываться при изменении объекта-состояния на стороне сервера.

Для демонстрации работы компонента создадим сущность Product с тремя атрибутами:

  • name типа String

  • minDiscount типа Double

  • maxDiscount типа Double

Затем сгенерируем стандартные экраны для данной сущности. Обратите внимание, что мы используем не универсальный компонент платформы, а "нативный" компонент Vaadin. Следовательно, экраны должны располагаться в модуле Web, а не в GUI - укажите это в окне генерации стандартных экранов.

В редакторе сущности мы хотим устанавливать минимальное и максимальное значение скидки с помощью компонента slider, который мы только что создали.

Перейдите к файлу product-edit.xml. Поля minDiscount и maxDiscount сделайте нередактируемыми, добавив к соответствующим элементам атрибут editable="false". Затем добавьте в fieldGroup новое кастомное поле slider.

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

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://editCaption"
        class="com.company.jscomponent.web.product.ProductEdit"
        datasource="productDs"
        focusComponent="fieldGroup"
        messagesPack="com.company.jscomponent.web.product">
    <dsContext>
        <datasource id="productDs"
                    class="com.company.jscomponent.entity.Product"
                    view="_local"/>
    </dsContext>
    <layout expand="windowActions"
            spacing="true">
        <fieldGroup id="fieldGroup"
                    datasource="productDs">
            <column width="250px">
                <field property="name"/>
                <field property="minDiscount" editable="false"/>
                <field property="maxDiscount" editable="false"/>
                <field id="slider" custom="true"/>
            </column>
        </fieldGroup>
        <frame id="windowActions"
               screen="editWindowActions"/>
    </layout>
</window>

Перейдите к файлу ProductEdit.java. Замените его содержимое следующим кодом:

package com.company.jscomponent.web.product;

import com.company.jscomponent.web.toolkit.ui.slider.SliderServerComponent;
import com.haulmont.cuba.gui.components.AbstractEditor;
import com.company.jscomponent.entity.Product;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.FieldGroup;
import com.haulmont.cuba.gui.components.VBoxLayout;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
import com.vaadin.ui.Layout;

import javax.inject.Inject;

public class ProductEdit extends AbstractEditor<Product> {

    @Inject
    private FieldGroup fieldGroup;

    @Inject
    private ComponentsFactory componentsFactory;

    @Inject
    private Datasource<Product> productDs;

    @Override
    protected void initNewItem(Product item) {
        super.initNewItem(item);
        item.setMinDiscount(15.0);
        item.setMaxDiscount(70.0);
    }

    @Override
    protected void postInit() {
        super.postInit();

        Component box = componentsFactory.createComponent(VBoxLayout.class);
        Layout vBox = (Layout) WebComponentsHelper.unwrap(box);
        SliderServerComponent slider = new SliderServerComponent();
        slider.setValue(new double[]{getItem().getMinDiscount(), getItem().getMaxDiscount()});
        slider.setMinValue(0);
        slider.setMaxValue(100);
        slider.setWidth("240px");
        slider.setListener(newValue -> {
            getItem().setMinDiscount(newValue[0]);
            getItem().setMaxDiscount(newValue[1]);
        });
        vBox.addComponent(slider);
        fieldGroup.getFieldNN("slider").setComponent(box);
    }
}

В методе initNewItem() мы проставляем начальные значения скидок для нового продукта.

В методе init() инициализируем кастомное поле для слайдера. Для компонента слайдера мы проставляем текущие значения, максимальное и минимальное значения, а также объявляем слушатель изменений значений. При движении ползунка мы будем проставлять новые значения скидок в соответствующие поля редактируемой сущности.

Запустите проект и откройте экран редактирования продукта. Изменение положения ползунка на слайдере должно изменять значение в соответствующем текстовом поле.

product edit