Composite component with inner components like a layout

I would like to create composite component based on a layout (e.g. vertical layout) which supports inner tags in the XML of the composite component. E.g.

comp:mycomp
<label … >
</comp:mycomp>

I am thinking of creating something like a popupView which allows one component inside the composite component which is then wrapped in a DIV that can be hidden or shown depending on a button click. So far the examples shown does not have an example of this, only components that that has configurable attributes. Can you show me an example of how to do this ?

Hello!

Basically, to solve your problem you just need for each sub-component recursively load each component via getting own component loader (registered in app context).

protected void createSubComponents(HasComponentsCustomComponent container, Element containerElement) {
        LayoutLoader loader = getLayoutLoader();

        for (Element subElement : containerElement.elements()) {
            ComponentLoader<?> componentLoader = loader.createComponentLoader(subElement);
            componentLoader.initComponent();
            pendingLoadComponents.add(componentLoader);

            container.add(componentLoader.getResultComponent());
        }
    }

Detailed solution

  1. Create custom component that implements HasComponents

    public class HasComponentsCustomComponent extends Composite<VerticalLayout> implements HasComponents {
     @Override
     public Element getElement() {
         return this.getContent().getElement();
     }
    }
    
  2. Add loader that also load inner components:

    public class HasComponentsCustomComponentLoader extends AbstractComponentLoader<HasComponentsCustomComponent> {
    
       protected List<ComponentLoader<?>> pendingLoadComponents = new ArrayList<>();
    
       @Override
       protected HasComponentsCustomComponent createComponent() {
           return factory.create(HasComponentsCustomComponent.class);
       }
    
       @Override
       public void initComponent() {
           super.initComponent();
           createSubComponents(resultComponent, element);
       }
    
       @Override
       public void loadComponent() {
           componentLoader().loadEnabled(resultComponent, element);
           loadSubComponents();
       }
    
       protected void loadSubComponents() {
           for (ComponentLoader<?> componentLoader : pendingLoadComponents) {
               componentLoader.loadComponent();
           }
    
           pendingLoadComponents.clear();
       }
    
       protected void createSubComponents(HasComponentsCustomComponent container, Element containerElement) {
           LayoutLoader loader = getLayoutLoader();
    
           for (Element subElement : containerElement.elements()) {
               ComponentLoader<?> componentLoader = loader.createComponentLoader(subElement);
               componentLoader.initComponent();
               pendingLoadComponents.add(componentLoader);
    
               container.add(componentLoader.getResultComponent());
           }
       }
    }
    
  3. Add to Application context that loader:

    @Configuration
    public class HasComponentsCustomComponentConfiguration {
    
       @Bean
       public ComponentRegistration hasComponentsCustomComponent() {
           return ComponentRegistrationBuilder.create(HasComponentsCustomComponent.class)
                   .withComponentLoader("hasComponentsCustomComponent", HasComponentsCustomComponentLoader.class)
                   .build();
       }
    
    }
    
  4. For test add your component into any of screens:

    <layout>
           <hasComponentsCustomComponent id="customComponent">
               <h1 text="aassss">
                   <h4 text="1"/>
                   <h4 text="2"/>
                   <h4 text="3"/>
               </h1>
               <h2 text="asdasdsadasdsadasd"/>
           </hasComponentsCustomComponent>
           ...
    </loader>
    
  5. Result

    image

Best regards,
Dmitry