Multifield AEM 6.5

Coral Multifield in AEM 6.5

Goal

Create a component that uses a multifield as simple as possible.

Procedure

Let’s create our dialog as follows:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
          jcr:primaryType="nt:unstructured"
          jcr:title="Multifield Component"
          sling:resourceType="cq/gui/components/authoring/dialog">
    <content jcr:primaryType="nt:unstructured"
             sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs"
                maximized="true">
                <items jcr:primaryType="nt:unstructured">
                    <general
                        jcr:primaryType="nt:unstructured"
                        jcr:title="General"
                        sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                        margin="true">
                        <items jcr:primaryType="nt:unstructured">
                            <column
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/container">
                                <items jcr:primaryType="nt:unstructured">
                                    <title
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                        fieldLabel="Title"
                                        name="./title"/>
                                    <subtitle
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                        fieldLabel="Subtitle"
                                        name="./subtitle"/>
                                    <accordion
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
                                        composite="{Boolean}true"
                                        fieldLabel="News">
                                        <field
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/container"
                                            name="./multifield">
                                            <items jcr:primaryType="nt:unstructured">
                                                <column
                                                    jcr:primaryType="nt:unstructured"
                                                    sling:resourceType="granite/ui/components/coral/foundation/container">
                                                    <items jcr:primaryType="nt:unstructured">
                                                        <description
                                                            jcr:primaryType="nt:unstructured"
                                                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                            fieldLabel="Description"
                                                            name="./description"/>
                                                    </items>
                                                </column>
                                            </items>
                                        </field>
                                    </accordion>
                                </items>
                            </column>
                        </items>
                    </general>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

The multifield input is inside the “accordion” tag, the tags “title” and “subtitle” are used just to give a little bit more context.

This is our dialog layout:

This is how our data are stored in the CRX:

And this is our data will be displayed, obviously I’m not using any kind of css style.

How can we write our html file? If you don’t need any kind data processing or computation, the clearest way is to create the following file. You can name it multifield.js.

"use strict";
use(["/libs/sightly/js/3rd-party/q.js"], function (Q) {
    var childProperties = Q.defer();
    granite.resource.resolve(granite.resource.path + "/" + this.multifieldName).then(function (currentResource) {
        currentResource.getChildren().then(function(child) {
            childProperties.resolve(child);
        });
    });
    return childProperties.promise;
});

And use it in your HTL code, as shown at line 1. Pass as parameter the node name where your data is stored, in this case “multifield”.

<sly data-sly-use.multifieldAccordion="${'multifield.js' @ multifieldName='multifield'}"></sly>
<sly data-sly-test.configured="${multifieldAccordion}"></sly>

<sly data-sly-test="${wcmmode.edit && !configured}">
    <div data-emptytext="${component.title}" class="cq-placeholder"></div>
</sly>
<div data-sly-test="${configured}" class="">
    <h4 class="">${properties.title} </h4>
    <p class="">${properties.subtitle}</p>
    <ul data-sly-list="${multifieldAccordion}">
       <li class="">${item.properties.description @ context='html'}</li>
    </ul>
</div>

If you prefer or you need to use a Sling Model, because of some processing (e.g. you need to resolve an internal URL), you can do it as follows.

package com.adobe.training.core.models.accordion;

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

import javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

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

    @Inject
    private String title;

    @Inject
    private String subtitle;

    @Inject
    @Named("multifield/.")
    private List<AccordionBulletPointModel> bulletPointList;

    public String getTitle() {
        return title;
    }

    public String getSubtitle() {
        return subtitle;
    }

    public List<AccordionBulletPointModel> getBulletPointList() {
        return bulletPointList;
    }

    public boolean isConfigured() {
        return bulletPointList != null && !bulletPointList.isEmpty();
    }

}

Honestly I really like to use the @Inject annotation, both for properties and children resources (as I did with @Named(“multifield/.”)). And instead of injecting a list of Resource, I prefer to inject immediately the adapted model so that you can easily access to its properties in the HTL code.

This is the model injected in the list:

package com.adobe.training.core.models.accordion;

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

import javax.inject.Inject;

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

    @Inject
    private String description;

    public String getDescription() {
        return description;
    }

}

You can add any kind of processing directly in the getter method.

Then, access to your data in the HTML in this way:

<sly data-sly-use.accordion="${'com.adobe.training.core.models.accordion.AccordionModel'}"></sly>
<sly data-sly-test.configured="${accordion.configured}"></sly>
<sly data-sly-test="${wcmmode.edit && !configured}">
    <div data-emptytext="${component.title}" class="cq-placeholder"></div>
</sly>
<div data-sly-test="${configured}" class="someClass">
    <h4 class="">${accordion.title}</h4>
    <p class="">${accordion.subtitle}</p>
    <ul data-sly-list="${accordion.bulletPointList}">
        <li class="">${item.description @ context='html'}</li>
    </ul>
</div>

That’s all! This has been tested on AEM 6.5 but I’m pretty sure that can work on AEM 6.4 and 6.3. Honestly I’m not sure in the previous versions because the sling:resourceType maybe will be different (not the coral one to be clear).

If you need to use a nested multifield, you have to implement a little bit more logic, but it’s really easy. Check it out here.

If you are looking at how to limit the number of items in a multifield, have a look here.

Cheers! 🍻

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: