Section 4.5.10.2, “Integration with Generic UI” covers the basics of native components integration into the Generic UI, which allows them to be declared in XML-descriptors and associated with data.
In the previous section, we have included the third-party Stepper component in the project. Integration of the IntStepper component, which implements
incremental change of int
numerical values, is described below.
-
Definition of component interface in the gui module:
package com.company.myproject.gui.components; import com.haulmont.cuba.gui.components.Field; public interface IntStepper extends Field { String NAME = "intStepper"; 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 chosen base interface for the component is
Field
, which allows data binding, i.e. the ability to view and edit an entity attribute. -
Component implementation in the web module:
package com.company.myproject.web.components; import com.company.myproject.gui.components.IntStepper; import com.haulmont.cuba.web.gui.components.WebAbstractField; public class WebIntStepper extends WebAbstractField<org.vaadin.risto.stepper.IntStepper> implements IntStepper { public WebIntStepper() { component = new org.vaadin.risto.stepper.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
WebAbstractField
. The base class implements the datasource bindingField
logic and other methods of theField
interface. -
Implementation of XML loader in the gui module:
package com.company.myproject.gui.loaders; import com.company.myproject.gui.components.IntStepper; import com.haulmont.cuba.gui.components.Component; import com.haulmont.cuba.gui.xml.layout.*; import com.haulmont.cuba.gui.xml.layout.loaders.AbstractFieldLoader; import org.dom4j.Element; public class IntStepperLoader extends AbstractFieldLoader { public IntStepperLoader(Context context, LayoutLoaderConfig config, ComponentsFactory factory) { super(context, config, factory); } @Override public Component loadComponent(ComponentsFactory factory, Element element, Component parent) { IntStepper component = (IntStepper) super.loadComponent(factory, element, parent); String manualInput = element.attributeValue("manualInput"); if (manualInput != null) { component.setManualInputAllowed(Boolean.valueOf(manualInput)); } String mouseWheel = element.attributeValue("mouseWheel"); if (mouseWheel != null) { component.setMouseWheelEnabled(Boolean.valueOf(mouseWheel)); } String stepAmount = element.attributeValue("stepAmount"); if (stepAmount != null) { component.setStepAmount(Integer.valueOf(stepAmount)); } String maxValue = element.attributeValue("maxValue"); if (maxValue != null) { component.setMaxValue(Integer.valueOf(maxValue)); } String minValue = element.attributeValue("minValue"); if (minValue != null) { component.setMinValue(Integer.valueOf(minValue)); } return component; } }
The
AbstractFieldLoader
class contains the loading logic for the basic properties of theField
component. It is sufficient to load the specific properties of theIntStepper
component only. -
Implementation of component palette in the web module:
package com.company.myproject.web; import com.company.myproject.gui.components.IntStepper; import com.company.myproject.gui.loaders.IntStepperLoader; import com.company.myproject.web.components.WebIntStepper; import com.haulmont.cuba.gui.ComponentPalette; import com.haulmont.cuba.gui.components.Component; import com.haulmont.cuba.gui.xml.layout.ComponentLoader; import java.util.HashMap; import java.util.Map; public class AppComponentPalette implements ComponentPalette { @Override public Map<String, Class<? extends ComponentLoader>> getLoaders() { Map<String, Class<? extends ComponentLoader>> loaders = new HashMap<>(); loaders.put(IntStepper.NAME, IntStepperLoader.class); return loaders; } @Override public Map<String, Class<? extends Component>> getComponents() { Map<String, Class<? extends Component>> components = new HashMap<>(); components.put(IntStepper.NAME, WebIntStepper.class); return components; } }
-
Registration of the component palette in the
App
class of the web module:package com.company.myproject.web; import com.haulmont.cuba.web.DefaultApp; import com.haulmont.cuba.web.gui.WebUIPaletteManager; public class App extends DefaultApp { static { WebUIPaletteManager.registerPalettes(new AppComponentPalette()); } }
-
Definition of the component XSD in the gui module:
<xs:schema targetNamespace="http://schemas.company.com/app/0.1/app-components.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.company.com/app/0.1/app-components.xsd" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xs:element name="intStepper"> <xs:complexType> <xs:attribute name="id" type="xs:string"/> <xs:attribute name="caption" type="xs:string"/> <xs:attribute name="width" type="xs:string"/> <xs:attribute name="height" type="xs:string"/> <xs:attribute name="datasource" 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>
-
Example of using the component inside a container:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/window.xsd" xmlns:app="http://schemas.company.com/app/0.1/app-components.xsd" caption="msg://editCaption" class="com.company.myproject.web.customer.CustomerEdit" datasource="customerDs" focusComponent="fieldGroup" messagesPack="com.company.myproject.web.customer"> <dsContext> <datasource id="customerDs" class="com.company.myproject.entity.Customer" view="_local"/> </dsContext> <layout expand="windowActions" spacing="true"> <app:intStepper id="stepper" datasource="customerDs" property="score" caption="Score" minValue="1" maxValue="20"/> <iframe id="windowActions" screen="editWindowActions"/> </layout> </window>
In the example above, the
intStepper
component is associated with thescore
attribute of theCustomer
entity, an instance of which is contained within thecustomerDs
data source. -
Example of using the component in a FieldGroup:
<dsContext> <datasource id="customerDs" class="com.company.myproject.entity.Customer" view="_local"/> </dsContext> <layout expand="windowActions" spacing="true"> <fieldGroup id="fieldGroup" datasource="customerDs"> <column width="250px"> <field id="name"/> <field id="score" custom="true" caption="Score"/> </column> </fieldGroup> ...
@Inject private ComponentsFactory componentsFactory; @Inject private FieldGroup fieldGroup; @Override public void init(Map<String, Object> params) { fieldGroup.addCustomField("score", new FieldGroup.CustomFieldGenerator() { @Override public Component generateField(final Datasource datasource, final String propertyId) { IntStepper stepper = componentsFactory.createComponent(IntStepper.NAME); stepper.setDatasource(datasource, propertyId); stepper.setWidth("100%"); return stepper; } }); }