Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.

Sling model for adapting partial filtered list of grand-children.

Avatar

Level 2

Hello Community members,

This new look feels great btw. Congrats to Adobe.

My question is regarding sling model for a resource that I want to adapt. I understand that resource to be adapted is able to inject a list of grandchildren. But is there a way to exclude or filter out some values based on a condition. Like example below. How do I write a Sling model that returns only grandchild nodes that have prop1 value = male.

|-- myresource(to be adapted)

|-- children

    |-- grandchild1 [prop1(name=gchildtype, value=male), prop2, prop3 and so on]

    |-- grandchild2 [prop1(name=gchildtype, value=female), prop2, prop3 and so on]

    |-- grandchild3 [prop1(name=gchildtype, value=male), prop2, prop3 and so on]

    |-- grandchild4 [prop1(name=gchildtype, value=female), prop2, prop3 and so on]

@Model(adaptables = Resource.class)

public class MaleMembers {

  @Inject

  private List<GrandChildClass> children;

 

  public List<GrandChildClass> getChildren() {

    return children;

  }

}

The above model will hold a list of all grandchildren. But how to get a partial list based on some condition like nodetype or any properties value, in this case gchildtype.

May be something like below could have been great:

@Inject @Filtered("gchildtype"="male")

  private List<GrandChildClass> children;

Or May be in the GrandChildClass model, we could do something like:

@Model(adaptables = Resource.class)

public class MaleGrandChildClass {

  @Inject @AdaptOnly("gchildtype"="male")

  private String gchildtype;

 

  public String getGchildtype() {

    return gchildtype;

  }

}

Feike Visser

9 Replies

Avatar

Level 10

This is a great question. I wish we had a good example on this. I will check.  We indeed need to expand our Sling Model examples.

Avatar

Level 10

Here is the Sling docs on this subject that may help-- Apache Sling - Sling Models.

In meantime - i am going to code this and what happens. 

Avatar

Level 2

I don't see current injectors/annotation can filter the list of grand child resources.

But sounds like what you are trying to do is to adapt your child resource to a list of filtered grand child resources, which can be achieved by creating a custom ChildModel, and then have a method to get a list of filtered grand child resources based on the property value.

Apache Sling - Sling Models

or you can create your own injector/annotation if you think that can be reused in multiple places

Avatar

Level 4

I believe there is nothing called native inside the sling model or AEM. You may need to create a custom logic for it. If I were you I would  have:

1. Created an OSGI Service which would have a injected a sling model (of your path)

2. A method that could get the node from that path using sling model

3. Method would then filter the result by traversing the node (use List Filters of Java)

4. Return the result.

#3 this method can be more generic with a custom logic to filter on values, parents, siblings, childs etc.

Avatar

Level 10

In this case - @Filter annotation did not filter the list based on the value of the property on the grandchilden nodes. I had to use Java application logic as suggested by navinkaushal

I used this app logic:

private List returnFemale(List list)

    {

      java.util.Iterator itr = list.iterator();

  

    List newList = new ArrayList();

      String myVal ="";

    

      try{

      while(itr.hasNext()) {

           

      //Cast to a Resource

      Resource myRes = (Resource)itr.next();

          

            Node myNode=  myRes.adaptTo(Node.class);

           

            //only keep a node if value is female

            myVal = myNode.getProperty("value").getString();

           

            if (myVal.compareTo("female") ==0) 

            {

            newList.add(myNode);

            }

             

           

         }

      }

      catch(Exception e)

      {

      e.printStackTrace() ;

      }

      return newList;

  

  

    }

The new list now only contains nodes where the value property is female. It works nicely. There are three grandchildren nodes where value is equal to female.

ANodeCollection.png

Sometimes when developing in AEM - it is best to use custom Java application logic using given APIs.

Avatar

Employee

I think in your example you need to implement a PostConstruct, and have the logic in there.

Avatar

Level 3

What you can do its just using script bindings, like that in HTL (seems to be legit way to do):

<sly data-sly-use.filteredGrandClindren="${'an.example.FilteredGrandChildren' @ filter='someproperty=somevalue'" />

<!-- use it somehow -->

Keep in mind that filter property here its just a custom one, so you can do it even with two parameters like property and value, who knows what is better!

Alright, so now there is a draft how to use it, but how to implement it? I won't write all class, believe that you can figure out most of placeholders I left you here:

@Model // all those stuff you need to make its model, adaptable should be a Request (fortunately or not)

public class FilteredGrandChildren {

@Self

@Via("resource")

private Resource resource; // so the resource that are you reffering to (lets name it a root)

@Inject

private String filter; // which is provided by script bindings - no worries, Sling should figure out which injector will use

public List<Resource> getFilteredChildren() {

  Pair<String, String> splittedFilter = splitFilterToPropertyNameAndValue(filter);

  return filter(getChildren(resource), splittedFilter.getLeft(), splittedFilter.getRight());

}

private List<Resource> getChildren(Resource resource) {} // essentially getting all children or grandchildrens, whatever you want

private List<Resource> filter(List<Resource> children, String propertyName, String propertyValue) { } // filtering collection

private Pair<String, String> splitFilterToPropertyNameAndValue(String filter) {} // obviously splitting by '='

Hope that it helps a little. That type of solution can be used across all project IMO and its not tightly coupled to any other model you should have.

Avatar

Level 2

Hello Feike Visser​ and smacdonald2008, navinkaushal​, eddiey39441077​, @puradawid .Thanks guys. Really appreciate.

Ofcourse doing an iteration on the list (and explicit filtering) is very obvious. But I want to avoid that as it will consume some extra time. Actually in my code I have already done that. My concern is regarding a huge list. {am i over-concerned here? or is it legit?}

Iterating (and or filtering) would be same as writing a listChildren(). And we would miss all fun and value that the Injects and the Adapts bring to the table. I was looking for a way that Sling could on-its-own (with some flag ofcourse) adapt the resource and inject only desired grandchildren.

I guess there isnt anything available to do that, like an Annotation or an Inject Strategy or something else.

Avatar

Level 10

i have not seen a good working example in the Sling Docs that would filter that list without explicit Java logic. Some say Sling Bindings - but i have not seen an actual example where that would filter the list.