5.8.6.1. Example of Using a Third-party Vaadin Component

Integration of third-party Vaadin components is described in Section 4.5.10.1, “Using Third-Party Vaadin Components”.

Below is the example of using the Stepper component, available at http://vaadin.com/addon/stepper. This component allows changing text field value in steps using the keyboard, mouse scroll or built-in up/down buttons.

  • Assuming that the project has a Customer entity with a String-type name attribute. An editor screen (customer-edit.xml ) was created in the web module with the following layout:

    <layout expand="windowActions"
            spacing="true">
        <fieldGroup id="fieldGroup"
                    datasource="customerDs">
            <column width="250px">
                <field id="name"/>
            </column>
        </fieldGroup>
        <iframe id="windowActions"
                screen="editWindowActions"/>
    </layout>

    Our task is to add an Integer-type score attribute for step editing to the screen.

  • Add the score attribute to the Customer entity in CUBA Studio:

    @Column(name = "SCORE")
    protected Integer score;
    
    public void setScore(Integer score) {
        this.score = score;
    }
    public Integer getScore() {
        return score;
    }

    Generate DB scripts and run the database update.

  • Run the Create web toolkit module command from the Project properties section of the Studio navigation panel.

  • Add a dependency on the component add-on to the web module in the project’s build.gradle:

    configure(webModule) {
        ...
        dependencies {
            ...
            compile("org.vaadin.addons:stepper:2.1.2")
        }
  • Re-create the IDE project files (Create or update IDEA project files menu item).

  • Include the add-on widget set in the AppWidgetSet.gwt.xml of the project’s web-toolkit module after the platform widget set:

    <module>
        <inherits name="com.haulmont.cuba.web.toolkit.ui.WidgetSet" />
        
        <inherits name="org.vaadin.risto.stepper.widgetset.StepperWidgetset" />
    
        <set-property name="user.agent" value="safari" />

    Building of widgets can be accelerated by setting the user.agent property. In this example, the widget set will be built only for WebKit-based browsers: Chrome, Safari, etc.

  • Add a custom score field to the fieldGroup component in the customer-edit.xml descriptor:

    <fieldGroup id="fieldGroup"
                datasource="customerDs">
        <column width="250px">
            <field id="name"/>
            <field id="score" custom="true" caption="Score"/>
        </column>
    </fieldGroup>
  • Add the following code to the CustomerEdit screen controller:

    public class CustomerEdit extends AbstractEditor<Customer> {
    
        @Inject
        private ComponentsFactory componentsFactory;
        @Inject
        private FieldGroup fieldGroup;
    
        private IntStepper stepper = new IntStepper();
    
        @Override
        public void init(Map<String, Object> params) {
            fieldGroup.addCustomField("score", new FieldGroup.CustomFieldGenerator() {
                @Override
                public Component generateField(final Datasource datasource, final String propertyId) {
                    Component box = componentsFactory.createComponent(BoxLayout.VBOX);
                    com.vaadin.ui.Layout layout = WebComponentsHelper.unwrap(box);
                    layout.addComponent(stepper);
                    stepper.setSizeFull();
    
                    stepper.addValueChangeListener(new Property.ValueChangeListener() {
                        @Override
                        public void valueChange(Property.ValueChangeEvent event) {
                            datasource.getItem().setValue(propertyId, event.getProperty().getValue());
                        }
                    });
    
                    return box;
                }
            });
        }
    
        @Override
        protected void postInit() {
            stepper.setValue(getItem().getScore());
        }
    }

    An instance of the component from the add-on is created in the stepper field. The init() method initializes the custom score field. The ComponentsFactory creates an instance of BoxLayout, retrieves a link to the Vaadin container via WebComponentsHelper, and adds the new component to it. The BoxLayout is then returned to be used in the custom field.

    Component data binding is implemented by setting its current value from the edited Customer in the postInit() method. Additionally, the corresponding entity attribute is updated through the implemented value change listener, when the user changes the value.

  • The new component can be used in any part of the screen outside of the FieldGroup. In order to do this, declare a container in the XML-descriptor:

    <hbox id="scoreBox"
          spacing="true">
        <label value="Score"/>
    </hbox>

    Inject the container in the screen controller, retrieve a link to the Vaadin container and add the component to it:

    public class CustomerEdit extends AbstractEditor<Customer> {
    
        @Inject
        private BoxLayout scoreBox;
    
        private IntStepper stepper = new IntStepper();
    
        @Override
        public void init(Map<String, Object> params) {
            com.vaadin.ui.Layout box = WebComponentsHelper.unwrap(scoreBox);
            box.addComponent(stepper);
    
            stepper.addValueChangeListener(new Property.ValueChangeListener() {
                @Override
                public void valueChange(Property.ValueChangeEvent event) {
                    getItem().setValue("score", event.getProperty().getValue());
                }
            });
        }
    
        @Override
        protected void postInit() {
            stepper.setValue(getItem().getScore());
        }
    }

    Data binding is implemented in the same way as described above.

  • To adapt the component style, create a theme extension in the project. Run the Create theme extension command in the Project properties section of the Studio navigation panel. After that, open the themes/havana/havana-ext.scss file in the web module and add the following code:

    @import "../havana/havana"; 
     
    @mixin havana-ext { 
      @include havana; 
     
      /* Basic styles for stepper */
      .v-stepper { 
        /* Use box-sizing: border-box; for all browsers */
        @include box-defaults; 
     
        height: 25px; 
        border: 0; 
     
        /* Use theme fonts */
        font-family: $theme_fonts; 
      } 
     
      /* Basic styles for inner text box */
      .v-stepper input[type="text"] { 
        /* Use box-sizing: border-box; for all browsers */
        @include box-defaults; 
     
        height: 25px; 
        padding: 1px; 
        outline: 0; 
        margin: 0; 
     
        /* Use border color from theme */
        border: 1px solid $theme_fieldBorderColor; 
      } 
     
      /* Focused styles */
      .v-stepper.v-stepper input[type="text"]:focus { 
        /* Use focused border color from theme */
        border-color: $theme_fieldFocusedBorderColor; 
        /* hide default focus outline */
        outline: 0; 
      } 
     
      /* Readonly styles */
      .v-readonly.v-stepper input[type="text"], 
      .v-readonly.v-stepper input[type="text"]:focus { 
        /* Use readonly border color from theme */
        border-color: $theme_fieldReadonlyBorderColor; 
      } 
    }