4.5.10.2. Integration with Generic UI

Integration of a native component into the generic user interface allows using such component in a large number of screens with little effort, just like the basic platform components. Full integration requires the following steps:

  1. Create the component interface. Interfaces are usually located in the GUI module, available to both client types – Web and Desktop. If the component should be implemented for one client type only, it can be placed in the Web or Desktop module directly. The example below implements the component for Web Client only.

    The component interface should be derived from com.haulmont.cuba.gui.components.Component or any of its inheritors, for example DatasourceComponent or Field:

    package com.company.myproject.gui.components;
    
    import com.haulmont.cuba.gui.components.Component;
    
    public interface MyComponent extends Component {
    
        String NAME = "myComponent";
    
        int getSomeParameter();
        void setSomeParameter(int value);
    }

    It is recommended to define the NAME constant in the interface. The constant should define the name of the component as a string, used for obtaining the component through the ComponentsFactory. This is also used as the name of the component’s XML element in the XML screen descriptors.

  2. Create the component implementation class in the web module.

    It is recommended to derive the class from com.haulmont.cuba.web.gui.components.WebAbstractComponent or one of its inheritors, for example WebAbstractField. A native component instance should be created in the class constructor, and the GUI interface calls should be delegated to it:

    package com.company.myproject.web.components;
    
    import com.company.myproject.gui.components.MyComponent;
    import com.haulmont.cuba.web.gui.components.WebAbstractComponent;
    
    public class WebMyComponent
            extends WebAbstractComponent<org.vaadin.someaddon.SomeComponent>
            implements MyComponent {
    
        public WebMyComponent() {
            component = new org.vaadin.someaddon.SomeComponent();
        }
    
        @Override
        public int getSomeParameter() {
            return component.getSomeParameter();
        }
    
        @Override
        public void setSomeParameter(boolean value) {
            component.setSomeParameter(value);
        }
    }

  3. Create a class implementing the ComponentPalette interface and return a map of custom components and their implementation classes from the getComponents() method:

    package com.company.myproject.web;
    
    import com.company.myproject.gui.components.MyComponent;
    import com.company.myproject.web.components.WebMyComponent;
    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 Component>> getComponents() {
            Map<String, Class<? extends Component>> components = new HashMap<>();
            components.put(MyComponent.NAME, WebMyComponent.class);
            return components;
        }
    
        @Override
        public Map<String, Class<? extends ComponentLoader>> getLoaders() {
            return Collections.emptyMap();
        }
    }

    The instance of the component palette must be registered in the application. This can be done in the App class initialization block:

    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());
        }
    }

  4. At this point, the new GUI component can be retrieved via the ComponentsFactory:

    @Inject
    private BoxLayout box;
    @Inject
    private ComponentsFactory componentsFactory;
    
    @Override
    public void init(Map<String, Object> params) {
        MyComponent myComponent = componentsFactory.createComponent(MyComponent.NAME);
        box.addComponent(myComponent);
        ...
    }

  5. In order to support component declaration in screen XML-descriptors, create a component loader class, implementing com.haulmont.cuba.gui.xml.layout.ComponentLoader. It is recommended to derive the loader class from com.haulmont.cuba.gui.xml.layout.loaders.ComponentLoader or any of its inheritors. The loader operates with the component GUI interface only, so it is common for all client types, and can be located in the gui module. The minimal implementation should call the loadComponent() method, which creates the component instance and sets its common properties, such as ID or size, taken from XML. Any custom component properties can be initialized afterwards:

    package com.company.myproject.gui.loaders;
    
    import com.company.myproject.gui.components.MyComponent;
    import com.haulmont.cuba.gui.components.Component;
    import com.haulmont.cuba.gui.xml.layout.*;
    import org.dom4j.Element;
    
    public class MyComponentLoader extends ComponentLoader {
    
        public MyComponentLoader(Context context, LayoutLoaderConfig config, ComponentsFactory factory) {
            super(context, config, factory);
        }
    
        @Override
        public Component loadComponent(ComponentsFactory factory, Element element, Component parent) {
            MyComponent component = (MyComponent) super.loadComponent(factory, element, parent);
    
            String someParameter = element.attributeValue("someParameter");
            if (someParameter != null) {
                component.setSomeParameter(Integer.valueOf(someParameter));
            }
            return component;
        }
    }

    The loader must be registered by the getLoaders() method of the previously created component palette:

    public class AppComponentPalette implements ComponentPalette {
        ...
    
        @Override
        public Map<String, Class<? extends ComponentLoader>> getLoaders() {
            Map<String, Class<? extends ComponentLoader>> loaders = new HashMap<>();
            loaders.put(MyComponent.NAME, MyComponentLoader.class);
            return loaders;
        }
    }

  6. Now the component can be used in XML-descriptors of your project:

    <layout>
        <myComponent id="someId" width="100%" someParameter="10"/>
    </layout>

    In order to enable autocomplete for component name and attributes in IDE, define your own XSD and include it in the screens:

    <window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
          xmlns:app="http://schemas.company.com/app/0.1/app-components.xsd"
          ...>
    
      <layout>
          <app:myComponent id="someId" width="100%" someParameter="10"/>
      </layout>

Section 5.8.6.2, “Example of Integrating a Vaadin Component into the Generic UI” covers the process of integrating the IntStepper component, used for changing integer values incrementally.