Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.
SOLVED

Fetch Coral3/Granite composite nested multifield(3 levels) in Design Dialog using Sling Model/Sightly

Avatar

Level 2

hi Team,

 

I am trying to fetch composite nested multifield values under Design Dialog implemented in coral3/Granite. I have tried using sling model but it is NOT able to inject the resource as the multifield values are stored under /etc/designs/PageXYZ/ComponentABC and am trying to call the model from the component path /apps/projectname/components/content/ModuleName/ComponentABC/ComponentABC.html.

 

Nested field storage looks like below:

+-/etc/designs/PageXYZ/ComponentABC

      | +- Item0

           | +-ABCItem

              | +-Item0

                | +-DEFItem

                  | +-Item 0

                      | +-GHIItem

                           | +-Item 0

                           | +-Item 1

 

       | +-Item 1

          | +- Hierarchy Similar to Item0

       | +-Item 2

          | +- Hierarchy Similar to Item0

 

Most of the examples i saw in the community posts are using dialogs and NOT design dialog. So, am NOT able to find the mistake i am doing with the sling modal. I have the flexibility to use JS Use API too, let me know in case of any workarounds related to that. My code is simmilar to this blog post https://helpx.adobe.com/experience-manager/using/aem64_htl_repeat_slingmodel.html but i am trying to retrieve it from design dialog instead of dialog.

 

Request your suggestion/inputs on the same.

1 Accepted Solution

Avatar

Correct answer by
Level 10

Hello, 

That's a very interesting question!  It actually inspired me to write a tutorial on nested Multifields. It is pending publication for the moment, but I'll add it as a comment later when it's out.

However this question goes a bit beyond what's in the tutorial so let me explain.

To access properties from the design dialog, you will need a Sling Model, like this one. Here is the interface:

import org.apache.sling.api.resource.Resource;

public interface DemoModel {
    Resource getPolicy();
}

And here is the implementation:

import com.day.cq.wcm.api.designer.Style;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;

import javax.annotation.PostConstruct;

@Model(
        adaptables = SlingHttpServletRequest.class,
        adapters = DemoModel.class
)
@Slf4j
public class DemoModelImpl implements DemoModel {

    @ScriptVariable
    private Style currentStyle;

    @Deleted Account
    private SlingHttpServletRequest request;

    @getter
    private Resource policy;

    @PostConstruct
    protected void init() {
        policy = request.getResourceResolver().getResource(currentStyle.getPath());
        if (policy == null) {
            log.error("Could not find policy");
            return;
        }
    }
}

Take a look at the important parts:

1. The @ScriptVariable private Style currentStyle line injects the component's design dialog properties. However in the case of a multifield, you will first need to convert currentStyle into the specific policy node, as I do in the init() method.

The policy Resource then represents the policy node in the JCR. For me its this one:

Selection_093.png

2. Now that we have the policy resource, we expose it for use in HTL using the getPolicy() method (see interface and @getter)

Now we can simply access the policy node and iterate over the nested multifield using HTL as you can see below:

<sly data-sly-use.model="com.theopendle.core.models.demo.DemoModel">
    <!--/* Get all child resources */-->
    <div data-sly-list.continentMultifield="${model.policy.getChildren}">

        <div data-sly-test="${continentMultifield.name == 'continents'}">
            <p>Continents</p>

            <ul data-sly-list.continent="${continentMultifield.getChildren}">
                <li>Name: ${continent.continentName}</li>
                <li>Countries:

                    <!--/* Get all child resources */-->
                    <div data-sly-list.countryMultifield="${continent.getChildren}">

                        <!--/* Keep only the countries multifield and iterate over its children */-->
                        <ul data-sly-test="${countryMultifield.name == 'countries'}"
                            data-sly-list.country="${countryMultifield.getChildren}">

                            <li>Name: ${country.countryName}</li>
                            <li>Code: ${country.code}</li>
                        </ul>
                    </div>
                </li>
            </ul>
        </div>
    </div>
</sly>

 Here is the result:

Selection_095.png

Hope that helped  

View solution in original post

4 Replies

Avatar

Correct answer by
Level 10

Hello, 

That's a very interesting question!  It actually inspired me to write a tutorial on nested Multifields. It is pending publication for the moment, but I'll add it as a comment later when it's out.

However this question goes a bit beyond what's in the tutorial so let me explain.

To access properties from the design dialog, you will need a Sling Model, like this one. Here is the interface:

import org.apache.sling.api.resource.Resource;

public interface DemoModel {
    Resource getPolicy();
}

And here is the implementation:

import com.day.cq.wcm.api.designer.Style;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;

import javax.annotation.PostConstruct;

@Model(
        adaptables = SlingHttpServletRequest.class,
        adapters = DemoModel.class
)
@Slf4j
public class DemoModelImpl implements DemoModel {

    @ScriptVariable
    private Style currentStyle;

    @Deleted Account
    private SlingHttpServletRequest request;

    @getter
    private Resource policy;

    @PostConstruct
    protected void init() {
        policy = request.getResourceResolver().getResource(currentStyle.getPath());
        if (policy == null) {
            log.error("Could not find policy");
            return;
        }
    }
}

Take a look at the important parts:

1. The @ScriptVariable private Style currentStyle line injects the component's design dialog properties. However in the case of a multifield, you will first need to convert currentStyle into the specific policy node, as I do in the init() method.

The policy Resource then represents the policy node in the JCR. For me its this one:

Selection_093.png

2. Now that we have the policy resource, we expose it for use in HTL using the getPolicy() method (see interface and @getter)

Now we can simply access the policy node and iterate over the nested multifield using HTL as you can see below:

<sly data-sly-use.model="com.theopendle.core.models.demo.DemoModel">
    <!--/* Get all child resources */-->
    <div data-sly-list.continentMultifield="${model.policy.getChildren}">

        <div data-sly-test="${continentMultifield.name == 'continents'}">
            <p>Continents</p>

            <ul data-sly-list.continent="${continentMultifield.getChildren}">
                <li>Name: ${continent.continentName}</li>
                <li>Countries:

                    <!--/* Get all child resources */-->
                    <div data-sly-list.countryMultifield="${continent.getChildren}">

                        <!--/* Keep only the countries multifield and iterate over its children */-->
                        <ul data-sly-test="${countryMultifield.name == 'countries'}"
                            data-sly-list.country="${countryMultifield.getChildren}">

                            <li>Name: ${country.countryName}</li>
                            <li>Code: ${country.code}</li>
                        </ul>
                    </div>
                </li>
            </ul>
        </div>
    </div>
</sly>

 Here is the result:

Selection_095.png

Hope that helped  

Avatar

Level 2

Thank you so much theop76211228 for your reply. I have already tried this approach of getting the currentStyle using @ScriptVariable. But whenever, i try to use that in HTL, i get an error saying "model cannot be correctly instantiated by the use api". Once I remove the @ScriptVariable , page loads properly. NOT sure what is this happening. Any inputs on that ?

?

 

Also, we are still NOT using editable templates so we do not have a policy node under /etc/designs. The storage of the multi-field is something like below.

Can I get the multi-field values like ${TopMultifield.getChildren} in HTL ?? What would be init method look like then ?

+-/etc/designs

  | +PageXYZ

      | +ComponentABC

       | +TopMultifield

         | +- Item0

           | +-ABCItem

              | +-Item0

                | +-DEFItem

                  | +-Item 0

                      | +-GHIItem

                           | +-Item 0

                           | +-Item 1

 

Sorry, too many questions.

 

Avatar

Level 10

No problem, I always enjoy tackling tricky issues

@ScriptVariable is a Sling feature not specific to AEM, so make sure that your model is adaptable to SlingHttpServletRequest.class, otherwise the injection won't work!

If that is not the issue then can you share the entire stacktrace for the instantiation error?

With regards to your second question: it depends. I've actually never used currentStyle outside of an Editable Template so I'm not sure what will happen. Let's try to get currentStyle injected properly first and see what resource it points to

Avatar

Level 2

Thanks theop76211228. I am able to resolve all my issues with your direction. @ScriptVariable was NOT the problem, when i try to do currentStyle.get("somedesign"), it throws an error of sling model NOT instantiating. NOT sure why. However, i solved the problem by following your code.

mycurrentnode_inside_etc_design = request.getResourceResolver().getResource(currentStyle.getPath());

And then in HTL,I iterated accordingly, though it was 4 level of iteration. LOL 

Thanks Again for your help.