Expand my Community achievements bar.

render AEM Component B into HTML of AEM component A

Avatar

Level 2

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.

13 Replies

Avatar

Level 5

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 2

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 2

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 2

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 2

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

Level 2

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