Extending the ACS AEM Commons Generic List to Support Multiple Fields

by Matthew Sullivan

Nov 27 , 2018

The Adobe Experience Manager ACS Commons Generic List facilitates authorable Title / Value pairs that can be used to populate drop downs from a centrally authored location. The functionality of the Generic List is limited to the Title and Value pairs, but there could be other use cases for centrally managed metadata that can be easily chosen from a dropdown. This example will extend the Generic List to manage metadata for organizational units / departments.

The Generic List implementation is final, so not extendable, but the underlying interface only relies on the jcr:title/value pairs to be present. We will use this and add additional fields that we can adaptTo our own implementation. The Generic List was built before the prevalence of Sling Models, so for this example we will try to simplify this by utilizing Sling Models.

Example Implementation on Github: https://github.com/msullivan-r2i/acs-genericlist-extension

Implementing the List Extension

department.java

This is the Sling Model that will add email and phone values to the existing jcr:title and value fields. It implements GenericList.Item, however this is not strictly required. The mechanisms that populate the dropdown will not use the Department implementation and rely on they underlying jcr:title & value fields and utilize the ACS implementation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Model(adaptables=Resource.class)
public class Department implements GenericList.Item {

    static final String TITLE_PREFIX = NameConstants.PN_TITLE + ".";
    
    @Inject
    private Resource resource;
    
    @Inject @Named("jcr:title") @Default(values="")
    public String title;
    
    @Inject @Default(values="")
    public String value;
    
    @Inject @Default(values="")
    public String phone;
    
    @Inject @Default(values="")
    public String email;
    
    public String getTitle() {
        return title;
    }
    
    public String getTitle(Locale locale) {
        /* see full file */
    }
    
    private String getLocalizedTitle(Locale locale) {
        return resource.getValueMap().get(TITLE_PREFIX + locale.toString().toLowerCase(), String.class);
    }
    
    public String getValue() {
        return value;
    }
    
    public String getPhone() {
        return phone;
    }
    
    public String getEmail() {
        return email;
    }

}



department-generic-list.html

This renders the item in the Generic List authoring page. The ACS Implementation uses JSP and not Sling Models, but this is a little simpler.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<sly data-sly-use.department="com.example.core.genericlist.Department">
<li>
<span data-sly-test="${!department.title}" style="color: red;">Please enter a title</span>
<sly data-sly-test="${department.title}">
Title: ${department.title} <br />
Value: ${department.value} <br />
Phone: ${department.phone} <br />
Value: ${department.email} <br />
</sly>
</li>
</sly>



dialog.xml

The Generic List is still Classic UI only, so we keep the jcr:title and value fields and add our email and phone fields.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Dialog"
title="Department - Generic List Item"
xtype="panel">
<items jcr:primaryType="cq:WidgetCollection">
<title
jcr:primaryType="cq:Widget"
fieldLabel="Title"
name="./jcr:title"
xtype="textfield"/>
<value
jcr:primaryType="cq:Widget"
fieldDescription="This is typically the text used internally or for URL generation"
fieldLabel="Value"
name="./value"
xtype="textfield"/>
<phone
jcr:primaryType="cq:Widget"
fieldLabel="Phone"
name="./phone"
xtype="textfield"/>
<email
jcr:primaryType="cq:Widget"
fieldLabel="Email Address"
name="./email"
xtype="textfield"/>
</items>
<listeners
jcr:primaryType="nt:unstructured"
afterrender="function() { ACS.CQ.GenericListItem.addTitleFields(this); }"/>
</jcr:root>

Usage

Department Helper

The DepartmentHelper provides two methods, one to get a specific Department model given a value. The other to give a List of all of the models.

A department and departments sample components are in the example project.

Department Component

The department component shows how to implement the dialog field to allow an author to choose a department from a dropdown. It then utilizes a WCMUsePojo helper to display the details of the selected department.

department/cq:dialog

We are referencing the ACS genericlist/datasource ui component to populate the dropdown, so this requires jcr:title and value fields be present for each item.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<department
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Department"
name="./department">
<datasource
jcr:primaryType="nt:unstructured"
sling:resourceType="acs-commons/components/utilities/genericlist/datasource"
path="/etc/acs-commons/lists/departments" />
</department>



department.html

The DepartmentHelper takes the value of the selected department from the dropdown and returns an adapted Department model.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<div class="cmp-department"
data-sly-use.departmentHelper="${'com.example.core.genericlist.DepartmentHelper' @ value=properties.department }"
data-sly-test.department="${departmentHelper.department}">
<h1>${department.title}</h1>
<ul>
<li>${department.phone}</li>
<li>${department.email}</li>
</ul>
</div>
<h1 data-sly-test="${!department}">Select a Department</h1>

Departments Component

The departments component iterates over all of the departments in the list in node order.

departments.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<div class="cmp-departments"
data-sly-use.departmentHelper="com.example.core.genericlist.DepartmentHelper"
data-sly-test.departments="${departmentHelper.allDepartments}"
data-sly-test.hasDepartments="${departments.size > 0}">
<div data-sly-list.department="${departments}">
<h1>${department.title}</h1>
<ul>
<li>${department.phone}</li>
<li>${department.email}</li>
</ul>
</div>
</div>
<h1 data-sly-test="${!hasDepartments}">Add departments to the department generic list named <pre>/etc/acs-commons/lists/departments</pre></h1>

Optional Implementation

The example also creates a new template that can be placed under /etc/acs-commons/genericlist and an /etc/designs/acs-genericlist-example design definition that allows our department-generic-list component to be added to that page template. There is also a page component that simply extends the acs genericlist page component to give the design something unique to bind to.

This is optional and the extended generic list item could be allowed through the standard design ui.

I18n

This implementation doesn’t consider I18n, and the ACS list seems to do this through the dictionary. If this is a requirement, perhaps moving these out of /etc and into /content/../locale could allow for translations of this metadata as long as the values stayed the same.

Author Bio

Matthew Sullivan is a Senior Lead Architect (AEM) at R2integrated.  He has worked in AEM/CQ for 10 years and holds AEM Architect and Developer certifications from Adobe.

R2i accelerates customer connections. In a complex world that’s always demanding more, R2i is a digital agency that helps marketers create more awareness, build deeper relationships, and drive measurable impact.

Tags: Adobe Experience Manager | AEM | Experience Manager | Adobe CQ | ACS AEM Commons | Generic List