3.5.17.3.2. Integrating a Vaadin Component into the Generic UI
In the previous section, we have included the third-party Stepper component in the project. In this section, we will integrate it into CUBA Generic UI. This will allow developers to use the component declaratively in the screen XML and bind it to the data model entities through Data Components.
In order to integrate Stepper into CUBA Generic UI, we need to create the following files:
-
Stepper
- an interface of the component in the gui subfolder of the web module. -
WebStepper
- a component implementation in the gui subfolder of the web module. -
StepperLoader
- a component XML-loader in the gui subfolder of the web module. -
ui-component.xsd
- a new component XML schema definition. If the file already exists, add the information about the new component to the existing file. -
cuba-ui-component.xml
- the file that registers a new component loader in web module. If the file already exists add the information about the new component to the existing file.
Open the project in the IDE.
Let’s create required files and make necessary changes.
-
Create the
Stepper
interface in the gui subfolder of the web module. Replace its content with the following code: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); }
The base interface for the component is
Field
, which is designed to display and edit an entity attribute. -
Create the
WebStepper
class - a component implementation in the gui subfolder of the web module. Replace its content with the following code: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); } }
The chosen base class is
WebV8AbstractField
, which implements the methods of theField
interface. -
The
StepperLoader
class in the gui subfolder of the web module loads the component from its representation in 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)); } } }
The
AbstractFieldLoader
class contains code for loading basic properties of theField
component. SoStepperLoader
loads only the specific properties of theStepper
component. -
The
cuba-ui-component.xml
file in the web module registers the new component and its loader. Replace its content with the following code:<?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>
-
The
ui-component.xsd
file in web module contains XML schema definitions of custom visual components. Add thestepper
element and its attributes definition.<?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>
Let’s see how to add the new component to a screen.
-
Either remove the changes made in the previous section or generate editor screen for the entity.
-
Add the
stepper
component to the editor screen. You can add it either declaratively or programmatically. We’ll examine both methods.-
Using the component declaratively in an XML descriptor.
-
Open the
customer-edit.xml
file. -
Define the new namespace
xmlns:app="http://schemas.company.com/agd/0.1/ui-component.xsd"
. -
Remove the
score
field fromform
. -
Add
stepper
component to the screen.
As a result, the XML descriptor should look like this:
<?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>
In the example above, the
stepper
component is associated with thescore
attribute of theCustomer
entity. An instance of this entity is managed by thecustomerDc
instance container. -
-
Programmatic creation of the component in a Java controller.
<?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); } }
-
-
Start the application server. The resulting editor screen will look as follows: