3.5.17.4.3. Using a JavaScript library

The CUBA platform has a Slider component that allows setting a numeric value within a defined range. For your tasks, you may need a similar component with two drag handlers that define a values range. So in this example, we will use the Slider component from the jQuery UI library.

Create a new project in CUBA Studio and name it jscomponent.

In order to use the Slider component, we need to create the following files:

  • SliderServerComponent - a Vaadin component integrated with JavaScript.

  • SliderState - a state class of the Vaadin component.

  • slider-connector.js - a JavaScript connector for the Vaadin component.

The process of integration into the Generic UI is the same as described at Integrating a Vaadin Component into the Generic UI, so we won’t repeat it here.

Let’s create required files in the toolkit/ui/slider subfolder of the web module and make necessary changes.

  • SlideState state class defines what data is transferred between the server and the client. In our case it is a minimal possible value, maximum possible value and selected values.

    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 server-side component SliderServerComponent.

    package com.company.jscomponent.web.toolkit.ui.slider;
    
    import com.haulmont.cuba.web.widgets.WebJarResource;
    import com.vaadin.annotations.JavaScript;
    import com.vaadin.ui.AbstractJavaScriptComponent;
    import elemental.json.JsonArray;
    
    @WebJarResource({"jquery:jquery.min.js", "jquery-ui:jquery-ui.min.js", "jquery-ui:jquery-ui.css"})
    @JavaScript({"slider-connector.js"})
    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;
        }
    }

    The server component defines getters and setters to work with the slider state and an interface of value change listeners. The class extends AbstractJavaScriptComponent.

    The addFunction() method invocation in the class constructor defines a handler for an RPC-call of the valueChanged() function from the client.

    The @JavaScript and @WebJarResource annotations point to files that must be loaded on the web page. In our example, these are JavaScript files of the jquery-ui library and the stylesheet for jquery-ui located in the WebJar resource, and the connector that is located in the Java package of the Vaadin server component.

  • JavaScript connector 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 0px");
    
        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);
        }
    }

    Connector is a function that initializes a JavaScript component when the web page is loaded. The function name must correspond to the server component class name where dots in package name are replaced with underscore characters.

    Vaadin adds several useful methods to the connector function. this.getElement() returns an HTML DOM element of the component, this.getState() returns a state object.

    Our connector does the following:

    • Initializes the slider component of the jQuery UI library. The slide() function is invoked when the position of any drag handler changes. This function in turn invokes the valueChanged() connector method. valuedChanged() is the method that we defined on the server side in the SliderServerComponent class.

    • Defines the onStateChange() function. It is called when the state object is changed on the server side.

To demonstrate how the component works, let’s create the Product entity with three attributes:

  • name of type String

  • minDiscount of type Double

  • maxDiscount of type Double

Generate standard screens for the entity. Ensure that the Module field is set to Module: 'app-web_main' (this field is shown only if the gui module is added to the project).

The slider component will set minimal and maximum discount values of a product.

Open the product-edit.xml file. Make minDiscount and maxDiscount fields not editable by adding the editable="false" attribute to the corresponding elements. Then add a box that will be used as a container for a Vaadin component.

As a result, the XML descriptor of the editor screen should look as follows:

<?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.jscomponent.web.product">
    <data>
        <instance id="productDc"
                  class="com.company.jscomponent.entity.Product"
                  view="_local">
            <loader/>
        </instance>
    </data>
    <dialogMode height="600"
                width="800"/>
    <layout expand="editActions" spacing="true">
        <form id="form" dataContainer="productDc">
            <column width="250px">
                <textField id="nameField" property="name"/>
                <textField id="minDiscountField" property="minDiscount" editable="false"/>
                <textField id="maxDiscountField" property="maxDiscount" editable="false"/>
                <hbox id="sliderBox" width="100%"/>
            </column>
        </form>
        <hbox id="editActions" spacing="true">
            <button action="windowCommitAndClose"/>
            <button action="windowClose"/>
        </hbox>
    </layout>
</window>

Open the ProductEdit.java file. Replace its content with the following code:

package com.company.jscomponent.web.product;

import com.company.jscomponent.entity.Product;
import com.company.jscomponent.web.toolkit.ui.slider.SliderServerComponent;
import com.haulmont.cuba.gui.components.HBoxLayout;
import com.haulmont.cuba.gui.screen.*;
import com.vaadin.ui.Layout;

import javax.inject.Inject;

@UiController("jscomponent_Product.edit")
@UiDescriptor("product-edit.xml")
@EditedEntityContainer("productDc")
@LoadDataBeforeShow
public class ProductEdit extends StandardEditor<Product> {

    @Inject
    private HBoxLayout sliderBox;

    @Subscribe
    protected void onInitEntity(InitEntityEvent<Product> event) {
        event.getEntity().setMinDiscount(15.0);
        event.getEntity().setMaxDiscount(70.0);
    }

    @Subscribe
    protected void onBeforeShow(BeforeShowEvent event) {
        SliderServerComponent slider = new SliderServerComponent();
        slider.setValue(new double[]{
                getEditedEntity().getMinDiscount(),
                getEditedEntity().getMaxDiscount()
        });
        slider.setMinValue(0);
        slider.setMaxValue(100);
        slider.setWidth("250px");
        slider.setListener(newValue -> {
            getEditedEntity().setMinDiscount(newValue[0]);
            getEditedEntity().setMaxDiscount(newValue[1]);
        });

        sliderBox.unwrap(Layout.class).addComponent(slider);
    }
}

The onInitEntity() method sets initial values for discounts of a new product.

Method onBeforeShow() initializes the slider component. It sets current, minimal and maximum values of the slider and defines the value change listener. When the drag handler moves, a new value will be set to the corresponding field of the editable entity.

Start the application server and open the product editor screen. Changing the drop handler position must change the value of the text fields.

product edit