AEM 6 SP2 - TouchUI Adding Dynamic Select Options

Goal


Add two Granite Select widgets - /libs/granite/ui/components/foundation/form/select with second select being dynamic. In this example, selecting language fills the country select widget with available countries for that language in CRX /libs/wcm/core/resources/languages

Demo | Package Install




Solution


1) Login to CRXDE Lite, create folder (nt:folder) /apps/touchui-fill-second-select

2) Create clientlib (type cq:ClientLibraryFolder/apps/touchui-fill-second-select/clientlib, set a property categories of String type to cq.authoring.dialog and dependencies of type String[] to underscore

3) Create file ( type nt:file ) /apps/touchui-fill-second-select/clientlib/js.txt, add the following

                         listener.js

4) Create file ( type nt:file ) /apps/touchui-fill-second-select/clientlib/listener.js, add the following code

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

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

    function adjustLayoutHeight(){
        //with only two selects, the second select drop down is not visible when expanded, so adjust the layout height
        //fixedcolumns i guess doesn't support css property height, so fallback to jquery
        //http://docs.adobe.com/docs/en/aem/6-0/develop/ref/granite-ui/api/jcr_root/libs/granite/ui/components/foundation/layouts/fixedcolumns/index.html
        $(".coral-FixedColumn-column").css("height", "20rem");
    }

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

        //http://docs.adobe.com/docs/en/aem/6-0/develop/ref/granite-ui/api/jcr_root/libs/granite/ui/components/foundation/form/select/index.html
        var language = new CUI.Select({
            element: $("[name='" + LANGUAGE +"']").closest(".coral-Select")
        });

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

        if(_.isEmpty(country) || _.isEmpty(language)){
            return;
        }

        var langCountries = {};

        //workaround to remove the options getting added twice, using CUI.Select()
        language._selectList.children().not("[role='option']").remove();

        function fillCountries(selectedLang, selectedCountry){
            country._select.children().remove();
            country._selectList.children().remove();

            _.each(langCountries, function(value, lang){
                if( (lang.indexOf(selectedLang) !== 0) || (value.country == "*") ){
                    return;
                }

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

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

            if(!_.isEmpty(selectedCountry)){
                country._select.val(selectedCountry).trigger('change');
            }
        }

        //listener on language select for dynamically filling the countries on language select
        language._selectList.on('selected.select', function(event){
            fillCountries(event.selectedValue);
        });

        //get the langs list
        $.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;
                }

                fillCountries(language.getValue(), data.country);
            })
        });
    });
})($, $(document));

5) Create a simple datasource for languages - /apps/touchui-fill-second-select/datasource/language/language.jsp


<%@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);
%>

7) The Touch UI dialog xml - /apps/touchui-fill-second-select/fill-second-select/cq:dialog

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" 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="nt:unstructured"
    jcr:title="Select TouchUI Component"
    sling:resourceType="cq/gui/components/authoring/dialog"
    helpPath="en/cq/current/wcm/default_components.html#Text">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/foundation/container">
        <layout
            jcr:primaryType="nt:unstructured"
            sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
        <items jcr:primaryType="nt:unstructured">
            <column
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/foundation/container">
                <items jcr:primaryType="nt:unstructured">
                    <fieldset
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Sample Select"
                        sling:resourceType="granite/ui/components/foundation/form/fieldset">
                        <layout
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
                        <items jcr:primaryType="nt:unstructured">
                            <column
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/foundation/container">
                                <items jcr:primaryType="nt:unstructured">
                                    <language
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/foundation/form/select"
                                        fieldLabel="Language"
                                        name="./language">
                                        <datasource
                                            jcr:primaryType="nt:unstructured"
                                            sling:resourceType="/apps/touchui-fill-second-select/datasource/language"
                                            addNone="{Boolean}true"/>
                                    </language>
                                    <country
                                        jcr:primaryType="nt:unstructured"
                                        sling:resourceType="granite/ui/components/foundation/form/select"
                                        fieldLabel="Country"
                                        name="./country"/>
                                </items>
                            </column>
                        </items>
                    </fieldset>
                </items>
            </column>
        </items>
    </content>
</jcr:root>


7 comments:

  1. Not able to see the dropdown .....

    ReplyDelete
  2. able to see the dropdown. But selection change is throwing on country._select. And we are planning to populate second dropdown base on the first dropdown selection. Can you please look into this.

    ReplyDelete
    Replies
    1. I literally copied these files and created a component. I get the same issue. May be this too does not work in AEM 6.2??

      Delete
  3. Hi Sreenkath,

    I have a multiselect dropdown in my dialog and I am getting the selected values as node properties within /content dir. Can i have the selected values as seperate nodes instead?

    ReplyDelete
  4. Please make this change in the listener.js at line 59 and this will work fine:
    //listener on language select for dynamically filling the countries on language select
    language._selectList.on('selected.select', function(event){
    fillCountries(event.selected);


    });

    ReplyDelete
    Replies
    1. Thanks, you saved my life. I wish you were my teammate.

      Delete