Expand my Community achievements bar.

SOLVED

How to obtain list of child components models in parent component sling model?

Avatar

Level 2

I have a container component and multiple 'card' child components. In the sling model of the container component i want to obtain a list of card component sling models.

Container component

   -card1
   -card2
   -card3
I want a list of card models to accessible in container component model so that in HTL i can iterate the list and access data of each card. How do i go about achieving this?

1 Accepted Solution

Avatar

Correct answer by
Level 5

Hi @Zendarkke 

 

If your container resource structure does contain an intermediate node that would in turn contain the card1, card2, card3 and so on, then the solutions you already received describe how you can map the children.

 

But if it does not, which I believe is your case, then the only thing you can do (as far as I know) is to write a code in your @PostConstruct method, get all the children from the current resource (the container), take only those matching the resourceType of a card and put in a list on the model class.

Not the best, but here is an example:

 

 

import java.util.List;
import java.util.Optional;

import javax.annotation.PostConstruct;

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;


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

    @Self
    private Resource container;

    private List<Resource> cards;

    @PostConstruct
    private void init() {
        Optional.ofNullable(container)
            .map(Resource::getChildren)
            .ifPresent(children -> children.forEach(child -> {
                if (child.isResourceType("your_card_resource_type")) {
                    cards.add(child);
                }
            }));
    }
}

 

 

 

Or if you want to complicate things a little more, I found this example also: https://www.jeroendruwe.be/aem-children-sling-model-annotation

View solution in original post

13 Replies

Avatar

Level 2

Hey , went through the example , just to clarify by using @ChildResource annotation above a List<childModel> , we should we able to get the list of all child nodes of that type?

Avatar

Level 2

From my understanding the @ChildResource annotation will give the mentioned resource only under the parent resource.
Instead we can make a CardModel, then use getter for List<CardModel> in the Container model.

Avatar

Level 2

If structure is like this

+Container
  +Cards
      + Card1
      + Card2
then you can use 
@ChildResource(name = "Cards")
private List<Resource> cardsResources;

Avatar

Level 2

This code snippet worked fine until i changed structure of my component
My container component is having a superType of accordion component so node content structure is 
+Container
 +card1
  +card2
model code snippet

@getter
@ChildResource
private List<ChildModel> children;

 However here it is failing to fetch child models when in container html write this code ${model.children} , it shows up as empty,any reason as to why this is failing

Avatar

Correct answer by
Level 5

Hi @Zendarkke 

 

If your container resource structure does contain an intermediate node that would in turn contain the card1, card2, card3 and so on, then the solutions you already received describe how you can map the children.

 

But if it does not, which I believe is your case, then the only thing you can do (as far as I know) is to write a code in your @PostConstruct method, get all the children from the current resource (the container), take only those matching the resourceType of a card and put in a list on the model class.

Not the best, but here is an example:

 

 

import java.util.List;
import java.util.Optional;

import javax.annotation.PostConstruct;

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;


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

    @Self
    private Resource container;

    private List<Resource> cards;

    @PostConstruct
    private void init() {
        Optional.ofNullable(container)
            .map(Resource::getChildren)
            .ifPresent(children -> children.forEach(child -> {
                if (child.isResourceType("your_card_resource_type")) {
                    cards.add(child);
                }
            }));
    }
}

 

 

 

Or if you want to complicate things a little more, I found this example also: https://www.jeroendruwe.be/aem-children-sling-model-annotation

Avatar

Level 2

But how do i create the intermediary node? Do i create an intermediary in the container node structure itself so that when i add child nodes its added under the intermediary or should this be dynamic on addition of child nodes?

Avatar

Community Advisor

Hi @Zendarkke 
You don't need to create intermediary node. The code @Tethich shared work on that content structure but you can adjust the code to read children content from immediate node as well.

 

Please check other code references. Also share the content node structure when you author for this container component so we will guide you better.



Arun Patidar

Avatar

Level 5

@Zendarkke 

 

1. When you already have significant content that has that container-->child structure, I don't think you should actually create an intermediate node. This might be a cumbersome operation, as you will need to write some code to alter all your places in content where you have such container. And more and more questions pop up when you want to do smth like this in general: How do I know I have modified all ? How do I monitor how my alteration goes ? Will there be any performance issues when data is too large ? Will there be any race condition issues when data is nested and I try to commit my changes ? .... and so on...

In this situation, you would need use the solution I proposed.

 

2. When you have no content (maybe you are in the development phase still), or small content at least, and you afford to make this change, you can add the intermediate node and later use @ChildResource in your model.

Avatar

Level 2

It is still in development phase. The node content structure is as below @arunpatidar @Tethich 
Container
  --child1

  --child2

I find the childResources annotation method to be easier but how do i go about making the node structure to have an intermediary node such as below, then i can directly target the intermediary node 
Container
 --childnodes
    --child1
    --child2

Avatar

Community Advisor

you can use the code shared by @Tethich , it is traversing the children nodes for 'container' and returning list of type Cards .

Have tried that code?

 

Container
  --child1

  --child2

 



Arun Patidar

Avatar

Level 5

Then, maybe you can try to use Granite Multifield that will add an child items under a parent node.