4.5.10.2. Интеграция компонентов в Generic UI

Интеграция "нативного" компонента в универсальный пользовательский интерфейс позволяет использовать его в большом количестве экранов с минимумом усилий - так же как и базовые компоненты платформы. Для полной интеграции необходимо выполнить следующие шаги:

  1. Создать интерфейс компонента. Обычно интерфейсы располагаются в модуле gui, чтобы быть доступными обоим типам клиентов - веб и десктоп. Если же вы уверены, что компонент будет реализован только для одного типа клиента, интерфейс можно расположить в соответствующем модуле - web или desktop. Далее предполагается что компонент реализован только для веб клиента.

    Интерфейс компонента должен быть унаследован от com.haulmont.cuba.gui.components.Component или какого-либо его наследника, например DatasourceComponent или 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);
    }

    В интерфейсе желательно определить константу NAME, содержащую строковое имя компонента для его получения через ComponentsFactory. Это же имя используется обычно как имя XML-элемента для работы с компонентом в XML-дескрипторах экранов.

  2. Создать класс имплементации компонента в модуле web.

    Класс компонента рекомендуется унаследовать от com.haulmont.cuba.web.gui.components.WebAbstractComponent или какого-либо его наследника, например WebAbstractField. В конструкторе класса создается экземпляр "нативного" компонента, и ему делегируются вызовы методов GUI-интерфейса:

    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. Создать класс, имплементирующий интерфейс ComponentPalette, и из его метода getComponents() вернуть мэп имен своих компонентов на их классы реализации:

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

    Экземпляр палитры компонентов необходимо зарегистрировать в приложении. Это можно сделать в блоке инициализации класса App:

    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. На данном этапе новый GUI-компонент доступен для получения через 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. Для поддержки объявления компонента в XML-дескрипторах экранов необходимо создать класс-загрузчик компонента, реализующий интерфейс com.haulmont.cuba.gui.xml.layout.ComponentLoader. Класс-загрузчик рекомендуется унаследовать от класса com.haulmont.cuba.gui.xml.layout.loaders.ComponentLoader или какого-либо его наследника. Загрузчик оперирует только с GUI-интерфейсом компонента, поэтому он является общим для всех типов клиентов и его можно разместить в модуле gui. В загрузчике достаточно вызвать унаследованный метод loadComponent(), который создает экземпляр компонента и устанавливает ему из XML общие свойства, такие как идентификатор, размеры и пр. После этого можно проинициализировать специфические свойства компонента:

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

    Для того, чтобы система нашла загрузчик, необходимо зарегистрировать его с помощью метода getLoaders() созданной ранее палитры компонентов:

    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. Теперь компонент можно использовать и в XML-дескрипторах экранов проекта:

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

    Для того, чтобы IDE подсказывала имя компонента и его атрибуты, можно определить собственную XSD и включать ее в экранах:

    <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>

В Раздел 5.8.6.2, «Пример интеграции компонента Vaadin в Generic UI» рассмотрен процесс интеграции в универсальный UI компонента IntStepper, предназначенного для пошагового изменения целого значения.