Expand my Community achievements bar.

SOLVED

render AEM Component B into HTML of AEM component A

Avatar

Level 3

Hi,

 

I ve got a AEM Component B, which I can drag into a page and configure it through Edit Dialog. This configured Values are loaded into SlingModelB, which is used in html code of Component B.

Now, I ve got another use case...
I ve got  a AEM Component A, which needs to dynamically include AEM Component B in html file of Component A.

How to create dynamically a SlingModelB and pass it to some HTL Command to render AEM Component B inside of Component A ?

 

Hope somebody is able to help me.

Thanks a lot for your support in advance.

 

 

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @vhochsteinTef , 

We can add any other component using data-sly-resource

Synatx:-

<div data-sly-resource="${'uniquename' @ resourceType='compoent or resource path'}"></div>
we can wrap it any html tag like div section or even sly if we don't want any wrapper tag

ModelA.java

 

package com.project-name-folder.core.models;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

/**
 * Sling model for the 'SSI requests' component business logic.
 */
@Model(adaptables = SlingHttpServletRequest.class,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ModelA {

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String description;

    public String getTitle() {
        return title;
    }

    public String getDescription() {
        return description;
    }
}

 

ModelB.java

 

package com.yours-project-package-name.core.models;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

/**
 * Sling model for the 'SSI requests' component business logic.
 */
@Model(adaptables = SlingHttpServletRequest.class,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ModelB {

    @ValueMapValue
    private String section1;

    @ValueMapValue
    private String section2;

    public String getSection1() {
        return section1;
    }

    public String getSection2() {
        return section2;
    }

}

 

componenta.html

 

<div data-sly-use.modelA="com.projectpackage-name.core.models.ModelA">
<h1 style="text-align:center">Cmp- A</h1>
<div style="background-color:yellow">
${modelA.title}
</div>
<div style="background-color:pink">
${modelA.description}
</div>
<div data-sly-resource="${'cmp-b' @ resourceType='/apps/pc/components/componentb'}"></div>
</div>

 

cq:dialog

 

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Component A"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs"
                maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <properties
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Properties"
                        sling:resourceType="granite/ui/components/coral/foundation/container"
                        margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <title
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Title"
                                                name="./title"/>
                                            <description
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Description"
                                                name="./description"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

 

componentb.html

 

<div data-sly-use.modelB="com.project-package-name.core.models.ModelB">
<h1 style="text-align:center">Cmp- B</h1>
<div style="background-color:green;width:45%;float:left">
${modelB.section1}
</div>
<div style="background-color:blue;width:45%;float:right">
${modelB.section2}
</div>
</div>

 

cq:dialog

 

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Component B"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs"
                maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <properties
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Properties"
                        sling:resourceType="granite/ui/components/coral/foundation/container"
                        margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <section1
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Section 1"
                                                name="./section1"/>
                                            <section2
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Section2 "
                                                name="./section2"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

 


Now if we add component A on page, it will be added with component b inside it 
we can author it from content tree or  directly hovering over component A and component B

MukeshYadav__0-1741364330763.png


Note:- Last but not the list use cq:isContainer property to true in component  A so that component B display just inside Component A in content tree. 

MukeshYadav__1-1741364866185.pngMukeshYadav__2-1741364944250.png

Thanks

View solution in original post

14 Replies

Avatar

Level 9

Hi @vhochsteinTef ,

To dynamically include Component B within Component A and pass a custom instance of SlingModelB to it, you can follow these steps:

1. Create Component A’s Sling Model (SlingModelA)
Component A will need a Sling Model to manage the logic of dynamically including Component B.

package com.example.core.models;

import com.example.core.models.SlingModelB;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;

import javax.inject.Inject;

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class SlingModelA {

    @Self
    private Resource resource;

    @Self
    private SlingModelB slingModelB;

    public SlingModelB getSlingModelB() {
        // Logic to initialize and customize SlingModelB
        return slingModelB;
    }
}

2. Component A HTL (HTML)
In the HTL of Component A, use the data-sly-resource attribute to dynamically include Component B and pass SlingModelB.  There are 2 examples how you can reuse sling models.  

<!-- componentA.html -->
<div class="component-a">
    <!-- Other HTML -->
    <!-- How to include component B from A -->
    <div data-sly-resource="${'path/to/componentB' @ resourceType='example/components/componentB'}">
    </div>
    
    <sly data-sly-use.slingModelA="com.example.core.models.SlingModelA"></sly>
    <!-- Example 1 -->
    <sly data-sly-use.slingModelB="com.example.core.models.SlingModelB"></sly>
    <!-- Example 2 -->
    <sly data-sly-set.slingModelBFromA="${slingModelA.slingModelB}"></sly>
    <!-- Other HTML -->
</div>

3. Component B Sling Model (SlingModelB)
Ensure SlingModelB is structured to accept dynamic data, possibly through constructor injection.

package com.example.core.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class SlingModelB {

    @ValueMapValue
    private String property1;

    // Other properties and logic

    public String getProperty1() {
        return property1;
    }

    // Other getters
}

4. Component B HTL (HTML)
Render the data passed from Component A inside Component B.

<!-- componentB.html -->
<div class="component-b">
    <p>${slingModelB.property1}</p>
    <!-- Render other properties of SlingModelB -->
</div>

 

Hi Konstantyn,

Thanks a lot for your guidance. Really appreciated.
I think I m still missing the major understanding how that link between data-sly-resource and data-sly-use is actually working.

Let me adjust the java model A code a little bit. It now returns a list of SlingModelBs
how would htl look like to render that list of SlingModelsBs in htl of SlingModelA ?

package com.example.core.models;

import com.example.core.models.SlingModelB;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;

import javax.inject.Inject;

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class SlingModelA {

    @Self
    private Resource resource;

    @Self
    private List<SlingModelB> slingModelBs;

    public List<SlingModelB> getSlingModelBs() {
        // Logic to initialize and customize SlingModelBs
        return slingModelBs;
    }
}


Thanks a lot for your support in advance.

--
Volker

Avatar

Community Advisor

Hi @vhochsteinTef 
Please check example here, where Text-and-Image component include text and image components based on condition : 

https://github.com/arunpatidar02/aemaacs-aemlab/blob/master/ui.apps/src/main/content/jcr_root/apps/a... 



Arun Patidar

Avatar

Level 3

Hi Arun,

in that example I do not understand how you can pass individual attributes for eg image resource.

 

 

 

--

Volker

Avatar

Community Advisor

Hi,

Lets consider you have a Component i.e. LinkModel(A), here is the sling Model for link : https://github.com/arunpatidar02/com.aemlab.junitapp/blob/master/core/src/main/java/com/aemlab/junit... 

Now your component LinkCollectionModel(B) use list of LinkModel(A) in the Component

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class LinkCollectionModel {

    @inject
    private Resource linkcollections;


    private List<LinkModel> linkCollectionList = Collections.emptyList();
    
    @PostConstruct
    protected void init() {
        if (linkcollections != null) {
            linkCollectionList = ModelHelper.getChildrenModels(linkcollections, LinkModel.class);
        }
    }

    public List<LinkModel> getLinkCollectionList() {
        return new ArrayList<LinkModel>(linkCollectionList);
    }

}

 

ModelHelper : 

https://github.com/arunpatidar02/com.aemlab.junitapp/blob/master/core/src/main/java/com/aemlab/junit... 

 

HTL

link.html

link-collection.html

link-template.html

<!-- Link: Component A -->
<div data-sly-use.link="com.aemlab.junit.core.models.LinkModel">
    <sly data-sly-use.linkTemplate="link.html">
        <sly data-sly-call="${linkTemplate.link @ link=link}"></sly>
    </sly>
</div>


<!-- LinkCollection: Component B -->
<div data-sly-use.linkCollection="com.aemlab.junit.core.models.LinkCollectionModel" class="cmp-link-collection__links-container">
    <ul data-sly-list.link="${linkCollection.linkCollectionList}">
        <sly data-sly-use.linkTemplate="link.html">
            <li>
                <sly data-sly-call="${linkTemplate.link @ link=link}"></sly>
            </li>
        </sly>
    </ul>
</div>

<!-- Link template -->
<sly data-sly-template.link="${@ link}">
    <div class="cmp__link">
        <a class="cmp-__link" 
           href="${link.lnHref @ context='uri'}" 
           title="${link.lnTitle}" 
           target="${link.lnTarget}">
           ${link.lnTitle}
        </a>
    </div>
</sly>

 



Arun Patidar

Avatar

Level 3

Hi Arun,

 

thanks a lot, in my use case I do not have a template I have AEM Component.
How to adapt your 

<sly data-sly-call="${linkTemplate.link @ link=link}"></sly>


to do the same with AEM Component B, so passing current Link Model to Component B ?

thanks a lot for your support in advance. 

Avatar

Community Advisor

Hi @vhochsteinTef 
You need to then use data-sly-resource to map a resource with another component.

<section data-sly-resource="${link.path @ resourceType='my/resource/type'}"></section>

 



Arun Patidar

Avatar

Level 3

Hi 

ok that would be easy... just to clarify..

Following code will be my solution right ?

<!-- LinkCollection: Component B -->
<div data-sly-use.linkCollection="com.aemlab.junit.core.models.LinkCollectionModel" class="cmp-link-collection__links-container">
    <ul data-sly-list.link="${linkCollection.linkCollectionList}">
            <li>
                <sly data-sly-resource="${link.path @ resourceType='my/resource/type'}"></sly>
            </li>
        </sly>
    </ul>
</div>

 

 

Avatar

Community Advisor

Hi @vhochsteinTef 
The above code is not gonna be your solution, the code is just for reference/demo to show how multiitem can be parsed and mapped to another component.

 

You need to refactor the HTL and test if something does not work with above HTL.

 

But concept is gonna be the same.



Arun Patidar

Avatar

Level 3

Hi Arun,

 

did not work out..

Model B is not passed to sly resource at least it looks like, cause just empty "html" of Component B is rendered by that call...

<sly data-sly-resource="${link.path @ resourceType='my/resource/type'}"></sly>
<sly data-sly-resource="${link @ resourceType='my/resource/type'}"></sly>


tried both these variations...

Avatar

Community Advisor

Avatar

Level 3

Hi Arun,

I am sure that correct html file of Model B is called, because that is rendered, just without any attribute Values of Model B.

 

In your examples given ....
item.path
what is that doing... just the property path of item is passed ?
I need the whole SlingModel B including all attributes

Avatar

Administrator

@vhochsteinTef Did you find the suggestions helpful? Please let us know if you require more information. Otherwise, please mark the answer as correct for posterity. If you've discovered a solution yourself, we would appreciate it if you could share it with the community. Thank you!



Kautuk Sahni

Avatar

Correct answer by
Community Advisor

Hi @vhochsteinTef , 

We can add any other component using data-sly-resource

Synatx:-

<div data-sly-resource="${'uniquename' @ resourceType='compoent or resource path'}"></div>
we can wrap it any html tag like div section or even sly if we don't want any wrapper tag

ModelA.java

 

package com.project-name-folder.core.models;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

/**
 * Sling model for the 'SSI requests' component business logic.
 */
@Model(adaptables = SlingHttpServletRequest.class,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ModelA {

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String description;

    public String getTitle() {
        return title;
    }

    public String getDescription() {
        return description;
    }
}

 

ModelB.java

 

package com.yours-project-package-name.core.models;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

/**
 * Sling model for the 'SSI requests' component business logic.
 */
@Model(adaptables = SlingHttpServletRequest.class,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class ModelB {

    @ValueMapValue
    private String section1;

    @ValueMapValue
    private String section2;

    public String getSection1() {
        return section1;
    }

    public String getSection2() {
        return section2;
    }

}

 

componenta.html

 

<div data-sly-use.modelA="com.projectpackage-name.core.models.ModelA">
<h1 style="text-align:center">Cmp- A</h1>
<div style="background-color:yellow">
${modelA.title}
</div>
<div style="background-color:pink">
${modelA.description}
</div>
<div data-sly-resource="${'cmp-b' @ resourceType='/apps/pc/components/componentb'}"></div>
</div>

 

cq:dialog

 

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Component A"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs"
                maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <properties
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Properties"
                        sling:resourceType="granite/ui/components/coral/foundation/container"
                        margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <title
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Title"
                                                name="./title"/>
                                            <description
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Description"
                                                name="./description"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

 

componentb.html

 

<div data-sly-use.modelB="com.project-package-name.core.models.ModelB">
<h1 style="text-align:center">Cmp- B</h1>
<div style="background-color:green;width:45%;float:left">
${modelB.section1}
</div>
<div style="background-color:blue;width:45%;float:right">
${modelB.section2}
</div>
</div>

 

cq:dialog

 

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Component B"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs"
                maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <properties
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Properties"
                        sling:resourceType="granite/ui/components/coral/foundation/container"
                        margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <section1
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Section 1"
                                                name="./section1"/>
                                            <section2
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                fieldLabel="Section2 "
                                                name="./section2"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

 


Now if we add component A on page, it will be added with component b inside it 
we can author it from content tree or  directly hovering over component A and component B

MukeshYadav__0-1741364330763.png


Note:- Last but not the list use cq:isContainer property to true in component  A so that component B display just inside Component A in content tree. 

MukeshYadav__1-1741364866185.pngMukeshYadav__2-1741364944250.png

Thanks