AEM 6.3 - Multifield using Sling Model

user972418 picture user972418 · Oct 1, 2017 · Viewed 8.2k times · Source

I am trying to create a Coral UI 3 multifield and use Sling Models. Here is how the dialog looks like: enter image description here

enter image description here

Here is the code :

    package com.aem.sites.models;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;

import org.apache.log4j.Logger;

import com.aem.sites.models.Header;

@Model(adaptables=Resource.class)
public class HeaderList {

    final static Logger logger = Logger.getLogger(HeaderList.class);

    @Inject
    @Named("header")
    public Resource headerList;

    public List<Header> links;

    @PostConstruct
    protected void init() {
        links = new ArrayList<Header>();
        if(headerList != null) {
            logger.info("value of header list is "+headerList);
            links = populateModel(links, headerList);
        }
    }

    public  List<Header> populateModel(List<Header> array, Resource resource) {
        if(resource != null) {
            Iterator<Resource> linkResource = resource.listChildren();
            while(linkResource.hasNext()) {
                Header header = linkResource.next().adaptTo(Header.class);
                if(header != null) {
                    array.add(header);
                }
            }
        }
        return array;
    }

    public List<Header> getLinks() {
        return links;
    }

}


    package com.aem.sites.models;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;

//Creating adaptable resource class using sling annotation 
@Model(adaptables = Resource.class)
public class Header {

    @Inject
    @Named("linkText")
    private String linkText;

    @Inject
    @Named("linkUrl")
    private String linkUrl;

    @PostConstruct
    protected void init() {

    }

    public String getTitle() {
        return linkText;
    }

    public String getUrl() {
        return linkUrl;
    }

    public void setTitle(String title) {
        linkText = title;
    }

    public void setUrl(String url) {
        linkUrl = url;
    }
}

and here is the HTL file:

<header id="header" class="skel-layers-fixed">
 <nav id="nav">
 <h1><a href="#">Ion</a></h1>
<sly data-sly-use.headerObj="com.aem.sites.models.HeaderList">
<ul data-sly-list="${headerObj.items.listChildren}">
<li><a href="${item.linkUrl @context='styleToken'}">${item.linkText @context='styleToken'}</a></li>
</ul>
</sly>
</nav>
</header>

and here is the error I have been getting :

Caused by: org.apache.sling.models.factory.MissingElementsException: Could not inject all required fields into class com.aem.sites.models.HeaderList
    at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:593)
    at org.apache.sling.models.impl.ModelAdapterFactory.internalCreateModel(ModelAdapterFactory.java:335)
    at org.apache.sling.models.impl.ModelAdapterFactory.createModel(ModelAdapterFactory.java:223)
    at org.apache.sling.scripting.sightly.models.impl.SlingModelsUseProvider.provide(SlingModelsUseProvider.java:135)
    at org.apache.sling.scripting.sightly.impl.engine.extension.use.UseRuntimeExtension.call(UseRuntimeExtension.java:72)
    ... 209 common frames omitted
    Suppressed: org.apache.sling.models.factory.MissingElementException: Could not inject public org.apache.sling.api.resource.Resource com.aem.sites.models.HeaderList.headerList
        at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:598)
        ... 213 common frames omitted
    Caused by: org.apache.sling.models.factory.ModelClassException: No injector returned a non-null value!
        at org.apache.sling.models.impl.ModelAdapterFactory.injectElement(ModelAdapterFactory.java:513)
        at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:596)

I am not sure what I am doing wrong here. I am not sure if it has something to do with the dialog ? That's what I suspect.

Any help is appreciated.

Thanks in advance.

Answer

mickleroy picture mickleroy · Oct 2, 2017

The error is thrown because the attribute headerList in your model cannot be injected successfully. You need to be able to fetch the items node so that you can then list it's children. Perhaps try the following:

@Model(adaptables=Resource.class)
public class HeaderList {

    final static Logger logger = Logger.getLogger(HeaderList.class);

    @Inject
    @Named("items") <--------- changed this
    public Resource headerList;

    public List<Header> links;

    @PostConstruct
    protected void init() {
        links = new ArrayList<Header>();
        if(headerList != null) {
            logger.info("value of header list is "+headerList);
            links = populateModel(links, headerList);
        }
    }

    public  List<Header> populateModel(List<Header> array, Resource resource) {
        if(resource != null) {
            Iterator<Resource> linkResource = resource.listChildren();
            while(linkResource.hasNext()) {
                Header header = linkResource.next().adaptTo(Header.class);
                if(header != null) {
                    array.add(header);
                }
            }
        }
        return array;
    }

    public List<Header> getLinks() {
        return links;
    }
}

and in your template, just use the links property:

<sly data-sly-use.headerObj="com.aem.sites.models.HeaderList">
  <ul data-sly-list="${headerObj.links}">
    <li><a href="${item.linkUrl @context='styleToken'}">${item.linkText @context='styleToken'}</a>
    </li>
  </ul>
</sly>