March 29, 2020
Estimated Post Reading Time ~

Dynamically Populate Drop Down Values in AEM

You must be thinking that what I am gonna talk about here if there are hundreds of blogs available over the internet about dynamic dropdown. So there are really some things which might be new to you and this blog won't disappoint you.

There are so many places in the AEM project where we need to dynamically populate dropdown, for instance, populate the list of pages, populate the country list, and so on.
There are two types of use cases that are being used in each and every project.

Dynamically populate a dropdown based on the data source (Build on logic)
Dynamically Populate dropdown based on values available statically using ACS Commons Generic list. (No logic)
Dynamically Populate Drop-Down Based On Datasource

Problem Statement: Let suppose I have a requirement of pulling all the children pages of a root path in the dropdown.
Solution:
Step 1:
Make a drop-down widget and create a data source node under it like this:

Fig -1 Data Source Node Under Select Widget

Step 2: Add a property “resourceType” in the data source which can be mapped to “paths” and “resourceTypes” of the servlet.

Fig -2 Add Resource Type Property In Data Source

Step3: Write a java class mapped to the data source and write a logic to dynamically populate values in the dropdown.

sling:resourceType of datasource = sling.servlet.paths of servlet
OR
sling:resourceType of datasource = sling.servlet.resourceTypes of servlet

package com.aem.project.core.servlets;

import com.adobe.cq.commerce.common.ValueMapDecorator;
import com.adobe.granite.ui.components.ds.DataSource;
import com.adobe.granite.ui.components.ds.SimpleDataSource;
import com.adobe.granite.ui.components.ds.ValueMapResource;
import com.day.crx.JcrConstants;
import org.apache.commons.collections4.iterators.TransformIterator;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import java.util.*;

@Component(
service = Servlet.class,
property = {
Constants.SERVICE_DESCRIPTION + "= Dynamic Drop Down",
"sling.servlet.resourceTypes=" + "/apps/dropDownLIsting"
})
public class DropDownServlet extends SlingSafeMethodsServlet {

private static Logger LOGGER = LoggerFactory.getLogger(DropDownServlet.class);

@Override
protected void doGet(SlingHttpServletRequest request,
SlingHttpServletResponse response) {
try {
ResourceResolver resourceResolver = request.getResourceResolver();
List<KeyValue> dropDownList = new ArrayList<>();
Resource pathResource = request.getResource();
String rootPath = pathResource.getChild("datasource").getValueMap().get("rootPath",String.class);
Resource resource = request.getResourceResolver().getResource(rootPath);
Iterator<Resource> iterator = resource.listChildren();
List<Resource> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
list.forEach(res -> {
//ValueMap valueMap = res.getValueMap();
String title = res.getName();
dropDownList.add(new KeyValue(title, title));
});
@SuppressWarnings("unchecked")
DataSource ds =
new SimpleDataSource(
new TransformIterator(
dropDownList.iterator(),
input -> {
KeyValue keyValue = (KeyValue) input;
ValueMap vm = new ValueMapDecorator(new HashMap<>());
vm.put("value", keyValue.key);
vm.put("text", keyValue.value);
return new ValueMapResource(
resourceResolver, new ResourceMetadata(),
JcrConstants.NT_UNSTRUCTURED, vm);
}));
request.setAttribute(DataSource.class.getName(), ds);

} catch (Exception e) {
LOGGER.error("Error in Get Drop Down Values", e);
}
}

private class KeyValue {

/**
* key property.
*/
private String key;
/**
* value property.
*/
private String value;

/**
* constructor instance intance.
*
* @param newKey -
* @param newValue -
*/
private KeyValue(final String newKey, final String newValue) {
this.key = newKey;
this.value = newValue;
}
}
}

Note: I have seen people writing a new servlet for every select but actually this is not required. We can get more control over the component if multiple select can use the same service.To make this point clear read the problem statements below and understood how we can solve this.

Let’s discuss a few more problem statements:
In a project there can be situations wherein component A, you want to pull drop down from Root X and in component B, you want to pull drop down from Root Y. Solution: you can add a property may be rootPath in a datasource node and fetch the value of that rootPath in the servet and show values.

There can be a situation that in component A you want to pull a list of pages but in component B you want to pull the list of countries.
Solution: You can add a property that may be selector as “pageListing” for fetching all the pages or “countryList” to fetch all the countries list and in the servlet call different functions to dynamically populate different values.

Fig -4

Check the code for the above problem statement on github from here.
See the demo video for more visibility over all the problem statements and solutions: Demonstration Video On How to Populate Drop Down Dynamically Based On DataSource:

Dynamically Populate Drop-Down Based On Values Available Statically
Using ACS Commons Generic List

If you are working in the AEM platform, you must be aware of ACS Commons and its features and here I am going to describe one feature which is Generic Lists.

Using generic lists, if you have some values and want to populate these values dynamically in drop down you can do. The benefit is if one day the author wants to add one more value, he can add one more value by himself.

Prerequisites: Your AEM instance must have an acs commons package.
Step1: Go to misadmin and select Generic Lists under acs commons.
Step2: Click on New.
Step3: Select the generic list template and click on Create.
Step4: Open the page and drag and drop a component named Generic List Item.

Fig -4

Step5: Add the Title and Value in the Generic List Item component.

Fig -5 Generic List Items

Title: The title will be visible to the author in the dropdown.
Value: Value is what gets stored corresponding to what text you selected from the dropdown. Now values are ready to populate in the dropdown component.

Make a drop-down widget and create a datasource node.
Add the following values in the datasource node.

Fig -6 Data Source Node for using ACS Commons Generic List

1. sling:resourceType="acs-commons/components/utilities/genericlist/datasource"
2. path="/etc/acs-commons/lists/country-list" //This path is the page path you created earlier and added generic list item.

This is how you can dynamically populate all the values in dropdown using generic lists.


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.