Article summary

Summary
Discusses how to create an AEM Touch UI component that has a Touch UI dialog where a drop-down field is dynamically updated with data returned from an AJAX call. 

A special thank you to Praveen Dubey for contributing an AEM package that is used in this article. It's great community members like Praveen that helps the overall AEM community. If you would like to contribute in this manner to help the AEM community, please reach out to the AEM community manager, Scott Macdonald (scottm@adobe.com).

A special thank you to Ratna Kumar Kotla for helping test this content to ensure it works. 

In addition to reading this article, it is also recommended that you watch this Ask the AEM Community Experts on Touch UI development.

Digital Marketing Solution(s) Adobe Experience Manager (Adobe CQ)
Audience
Developer (beginner - intermediate)
Required Skills
JCR nodes, JavaScript/JQuery, HTML
Tested On Adobe Experience Manager 6.1

Note:

You can download an AEM package that contains the code used in this article. Download the package and deploy using package manager. The purpose of this code is to show the community these concepts in action. That is, it's to illustrate how to write an AEM Touch UI component that has a drop-down field that is dynamically populated with data returned  from an AJAX call. This community code is for teaching purposes only and not meant to go into production as is. You can view the sample community application by using the following URL: http://localhost:4502/editor.html/content/sightlymf.html (assuming you deploy on author).

Download

Note:

If you are not familiar with how to write a Touch UI component for AEM, then it is recommended that you read the following article: Creating your first Adobe Experience Manager Touch UI component

Introduction

You can create an Adobe Experience Manager (AEM) 6 Touch UI component that updates a drop-down field (located in the component dialog) based on a selection of another drop-down field. For example, assume that a user selects a language code from a Language drop-down field. This results in a Country drop-down field being updated with a list of countries based on the selection of the Language drop-down field, as shown in this illustration. 

TouchUI_DyamanicSelect
A Touch UI dialog that dynamically updates a field based on the selection of another field

This article discusses how to use an AJAX call to the following AEM endpoint to get the data based on the selection of the first dropdown field: 

http://localhost:4502/libs/wcm/core/resources/languages.2.json

This AJAX call returns data in the following format: 

{"jcr:primaryType":"nt:unstructured","language":"Arabic","defaultCountry":"ar_ae","country":"*"},"ar_ae":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"United Arab Emirates"},"ar_bh":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"Bahrain"},"ar_dz":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"Algeria"},"ar_eg":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"Egypt"},"ar_il":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"Israel"},"ar_iq":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"Iraq"},"ar_jo":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"Jordan"},"ar_kw":{"jcr:primaryType":"nt:unstructured","language":"Arabic","country":"Kuwait"},"ar_lb":

Install the AEM package

Install the AEM packages that are shown at the start of this community article. For information, see INSTALLING PACKAGES.

Once you install these packages, you can view the following project files.

projectFiles
AEM Project files

The following table describes the project files in the AEM package. 

Section Description
A A Script that defines a DataSource object that is used to populate the drop-down field in a Touch UI dialog. For more information, see this community article: Using Granite DataSource objects to populate AEM Touch UI objects.
B A ClientLib folder that contains script that makes an AJAX call that returns data used to populate the DataSource object - which is used to dynamically update the drop-down field in the Touch UI dialog.
C JCR nodes that represent a Touch UI dialog
B An HTML file that contains Sightly code.

AEM Touch UI dialog

The AEM component's dialog in this article lets an author select a Language value from the Language drop-down. Depending on the value that is selected, the Country down-down field is dynamically populated with a list of countries. The following illustrations shows the JCR nodes that comprise the component's Touch UI dialog. 

DialogNodes
JCR Nodes that make up the component's Touch UI dialog

Note:

For information about using CRXDE lite to create a Touch UI dialog, refer to this AEM community article: Creating your first Adobe Experience Manager Touch UI component.

In this example, the sling:resourceType of the language/datasource node is /apps/atesightly/components/content/datasource/language. This references a script in a clientlibs folder that defines the Grainte DataSource object (this is discussed later in this article).

The sling:resourceType of the items/country node is a granite/ui/components/foundation/form/select. This is a drop-down field in a Touch UI dialog that is dynamically populated. For information, see Select

 

Define a DataSource object

The Dynamic Select Component contains an AEM folder located at the following location that defines the DataSouce object. 

/apps/atesightly/components/content/datasource/language

The filename of the script is language.jsp. In this script, notice a Map object is created that defines the values that show up in the Language drop-down field. 

 

final Map<String, String> languages = new LinkedHashMap<String, String>();

    languages.put("ar", "Arabic");
    languages.put("en", "English");
    languages.put("de", "German");

Now that the Map object is populated, it is used to create a DataSource object. For information, see DataSource.

The following code shows how the Map object, named languages, is used to create the DataSource object. 

 

 

 DataSource ds = new SimpleDataSource(new TransformIterator(languages.keySet().iterator(), new Transformer() {
        public Object transform(Object o) {
            String language = (String) o;
            ValueMap vm = new ValueMapDecorator(new HashMap<String, Object>());

            vm.put("value", language);
            vm.put("text", languages.get(language));

            return new ValueMapResource(resolver, new ResourceMetadata(), "nt:unstructured", vm);
        }
    }));

The following code represents the language.jsp that creates the DataSource object. 

<%@include file="/libs/granite/ui/global.jsp"%>

<%@ page import="com.adobe.granite.ui.components.ds.DataSource" %>
<%@ page import="com.adobe.granite.ui.components.ds.ValueMapResource" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="org.apache.sling.api.wrappers.ValueMapDecorator" %>
<%@ page import="com.adobe.granite.ui.components.ds.SimpleDataSource" %>
<%@ page import="org.apache.commons.collections.iterators.TransformIterator" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.LinkedHashMap" %>
<%@ page import="org.apache.commons.collections.Transformer" %>
<%@ page import="org.apache.sling.api.resource.*" %>

<%
    final Map<String, String> languages = new LinkedHashMap<String, String>();

    languages.put("ar", "Arabic");
    languages.put("en", "English");
    languages.put("de", "German");

    final ResourceResolver resolver = resourceResolver;

    DataSource ds = new SimpleDataSource(new TransformIterator(languages.keySet().iterator(), new Transformer() {
        public Object transform(Object o) {
            String language = (String) o;
            ValueMap vm = new ValueMapDecorator(new HashMap<String, Object>());

            vm.put("value", language);
            vm.put("text", languages.get(language));

            return new ValueMapResource(resolver, new ResourceMetadata(), "nt:unstructured", vm);
        }
    }));

    request.setAttribute(DataSource.class.getName(), ds);
%>

Dynamic Select Component ClientLib

The component's clientlibs folder is located here:

/apps/atesightly/components/content/dynamic-select/clientlib

This clientlibs folder contains a script, named listener.js, that performs an AJAX call that retrieves JSON data that is used to populate the Country drop-down field in the Touch UI dialog. The call is made in the JQeury document ready event handler: 

$document.on("dialog-ready", function() {

Notice that the Select (drop-down field) is created:

 // Initializing country drop down field       

var country = new CUI.Select({            element: $("[name='" + COUNTRY +"']").closest(".coral-Select")        });

Note:

For information, see Coral-UI.

The script makes an AJAX call to retrieve a list of countries that correspond to the selected langauge value:

// Get the languages list from the source
$.getJSON("/libs/wcm/core/resources/languages.2.json").done(function(data){
langCountries = data;

var $form = country.$element.closest("form");

//get the second select box (country) saved value
$.getJSON($form.attr("action") + ".json").done(function(data){
if(_.isEmpty(data)){
return;
}

// Passing values to populate countries list
fillCountries(language.val(), data.country);
})
});

The following code represents the entire JS file in the clientlibs folder. 

(function ($, $document) {
    "use strict";

    var LANGUAGE = "./language", COUNTRY = "./country";

    function adjustLayoutHeight(){
        $(".coral-FixedColumn-column").css("height", "20rem");
    }
    
    $document.on("dialog-ready", function() {
        adjustLayoutHeight();
        
        // Getting reference of language drop down field
        var language = $("[name='" + LANGUAGE +"']").closest(".coral-Select")
        
        // Initializing country drop down field
        var country = new CUI.Select({
            element: $("[name='" + COUNTRY +"']").closest(".coral-Select")
        });
        
        if(_.isEmpty(country) || _.isEmpty(language)){
            return;
        }
        
        var langCountries = {};
        
        country._selectList.children().not("[role='option']").remove();
        
        function fillCountries(selectedLang, selectedCountry){

            var x = $("[name='./country']").closest(".coral-Select").find('option').remove().end();
            _.each(langCountries, function(value, lang) {

                if( (lang.indexOf(selectedLang) !== 0) || (value.country == "*") ){
                    return;
                }

                var test2 = $("[name='./country']")[0];

                $("<option>").appendTo(test2).val(lang).html(value.country);

            });

            country = new CUI.Select({
                element: $("[name='" + COUNTRY +"']").closest(".coral-Select")
            });
            
            
            if(!_.isEmpty(selectedCountry)){
                
                country.setValue(selectedCountry);
                
            }
            
        }
        
        //listener on language select for dynamically filling the countries on language select
        language.on('selected.select', function(event){
            console.log(event);
            fillCountries(event.selected);
        });
        
        // Get the languages list from the source
        $.getJSON("/libs/wcm/core/resources/languages.2.json").done(function(data){
            langCountries = data;
            
            var $form = country.$element.closest("form");
            
            //get the second select box (country) saved value
            $.getJSON($form.attr("action") + ".json").done(function(data){
                if(_.isEmpty(data)){
                    return;
                }

                // Passing values to populate countries list
                fillCountries(language.val(), data.country);
            })
        });
        
        
    });
})($, $(document));

Sightly code

The Sightly code is located in this file:

/apps/atesightly/components/content/dynamic-select/dynamic-select.html

The following represents this code.

Author the component

<p>Country Code: ${properties.language} </p>
<p>Language Code: ${properties.country}</p>

See also

Congratulations, you have just created an AEM 6 Touch UI component. Please refer to the AEM community page for other articles that discuss how to build AEM services/applications.

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License  Twitter™ and Facebook posts are not covered under the terms of Creative Commons.

Legal Notices   |   Online Privacy Policy