3.5.15.3.3. Using a JavaScript library
In this example, we will use the Slider component from the jQuery UI library. The slider will have two drag handlers that define a values range.
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 thevalueChanged()
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. Theslide()
function is invoked when the position of any drag handler changes. This function in turn invokes thevalueChanged()
connector method.valuedChanged()
is the method that we defined on the server side in theSliderServerComponent
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 ProductEit.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.