Sling model for adapting partial filtered list of grand-children. | Community
Skip to main content
Level 2
June 1, 2017
Question

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

  • June 1, 2017
  • 9 replies
  • 4317 views

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

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.

9 replies

smacdonald2008
Level 10
June 1, 2017

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.

smacdonald2008
Level 10
June 1, 2017

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. 

Level 2
June 1, 2017

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

navinkaushal
Level 4
June 1, 2017

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.

smacdonald2008
Level 10
June 1, 2017

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.

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

Feike_Visser1
Adobe Employee
Adobe Employee
June 2, 2017

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

Level 3
June 2, 2017

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.

Level 2
June 2, 2017

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.

smacdonald2008
Level 10
June 2, 2017

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.