Expand my Community achievements bar.

SOLVED

Dynamically update a dropdown list in AEM Touch UI dialog

Avatar

Level 2

I'm creating a custom AEM component using Touch UI dialogs. I have two dropdown fields: Country and State. I want the State dropdown options to change based on the selected Country. How can I implement this using Granite UI?

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @OmarKh3,

To implement cascading dropdowns in Touch UI dialogs, you can use the Granite UI datasource approach with JavaScript listeners (using coral-change event) and a servlet to populate the dependent dropdown dynamically.

1. Define your dialog with dynamic state dropdown

Create your cq:dialog structure like below:

<country
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="Country"
    name="./country"
    id="countrySelect">
    <items jcr:primaryType="nt:unstructured">
        <us
            jcr:primaryType="nt:unstructured"
            text="USA"
            value="usa"/>
        <ca
            jcr:primaryType="nt:unstructured"
            text="Canada"
            value="canada"/>
    </items>
</country>

<state
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="State"
    name="./state"
    datasource="/bin/getstates"
    id="stateSelect"/>
 

2. Create a servlet to return state options

Java servlet at /bin/getstates: (Just for your reference)

@SlingServlet(paths = "/bin/getstates", methods = "GET", extensions = "json")
public class StateDropdownServlet extends SlingAllMethodsServlet {
    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {

        String country = request.getParameter("country");
        List<JsonObject> states = new ArrayList<>();

        if ("usa".equals(country)) {
            states.add(new JsonObject().put("text", "California").put("value", "ca"));
            states.add(new JsonObject().put("text", "Texas").put("value", "tx"));
        } else if ("canada".equals(country)) {
            states.add(new JsonObject().put("text", "Ontario").put("value", "on"));
            states.add(new JsonObject().put("text", "Quebec").put("value", "qc"));
        }

        response.setContentType("application/json");
        response.getWriter().write(states.toString());
    }
}
 

3. Add a clientlib to handle dropdown change

(function(document, $) {
    $(document).on("change", "#countrySelect", function() {
        const selectedCountry = $(this).val();
        const stateSelect = $("#stateSelect");

        $.getJSON("/bin/getstates?country=" + selectedCountry, function(data) {
            stateSelect.empty();
            $.each(data, function(i, item) {
                stateSelect.append(
                    $("<coral-select-item>").val(item.value).text(item.text)
                );
            });
        });
    });
})(document, Granite.$);

Include this in a clientlib (category: cq.authoring.dialog) for your component.

Hope that helps!


Santosh Sai

AEM BlogsLinkedIn


View solution in original post

3 Replies

Avatar

Correct answer by
Community Advisor

Hi @OmarKh3,

To implement cascading dropdowns in Touch UI dialogs, you can use the Granite UI datasource approach with JavaScript listeners (using coral-change event) and a servlet to populate the dependent dropdown dynamically.

1. Define your dialog with dynamic state dropdown

Create your cq:dialog structure like below:

<country
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="Country"
    name="./country"
    id="countrySelect">
    <items jcr:primaryType="nt:unstructured">
        <us
            jcr:primaryType="nt:unstructured"
            text="USA"
            value="usa"/>
        <ca
            jcr:primaryType="nt:unstructured"
            text="Canada"
            value="canada"/>
    </items>
</country>

<state
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="State"
    name="./state"
    datasource="/bin/getstates"
    id="stateSelect"/>
 

2. Create a servlet to return state options

Java servlet at /bin/getstates: (Just for your reference)

@SlingServlet(paths = "/bin/getstates", methods = "GET", extensions = "json")
public class StateDropdownServlet extends SlingAllMethodsServlet {
    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {

        String country = request.getParameter("country");
        List<JsonObject> states = new ArrayList<>();

        if ("usa".equals(country)) {
            states.add(new JsonObject().put("text", "California").put("value", "ca"));
            states.add(new JsonObject().put("text", "Texas").put("value", "tx"));
        } else if ("canada".equals(country)) {
            states.add(new JsonObject().put("text", "Ontario").put("value", "on"));
            states.add(new JsonObject().put("text", "Quebec").put("value", "qc"));
        }

        response.setContentType("application/json");
        response.getWriter().write(states.toString());
    }
}
 

3. Add a clientlib to handle dropdown change

(function(document, $) {
    $(document).on("change", "#countrySelect", function() {
        const selectedCountry = $(this).val();
        const stateSelect = $("#stateSelect");

        $.getJSON("/bin/getstates?country=" + selectedCountry, function(data) {
            stateSelect.empty();
            $.each(data, function(i, item) {
                stateSelect.append(
                    $("<coral-select-item>").val(item.value).text(item.text)
                );
            });
        });
    });
})(document, Granite.$);

Include this in a clientlib (category: cq.authoring.dialog) for your component.

Hope that helps!


Santosh Sai

AEM BlogsLinkedIn


Avatar

Community Advisor

Hi @OmarKh3 ,

Try below solution:

1. Touch UI Dialog Setup

In your component’s cq:dialog, define two dropdowns: one static (country), and one dynamic (state):

<country
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="Country"
    name="./country"
    id="countrySelect">
    <items jcr:primaryType="nt:unstructured">
        <us
            jcr:primaryType="nt:unstructured"
            text="USA"
            value="usa"/>
        <ca
            jcr:primaryType="nt:unstructured"
            text="Canada"
            value="canada"/>
    </items>
</country>

<state
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/select"
    fieldLabel="State"
    name="./state"
    id="stateSelect"/>

Keep state dropdown empty for now it will be populated dynamically using JS.

2. Create the Servlet

Create a Sling Servlet that returns states as JSON based on the selected country.

@Component(service = Servlet.class,
           property = {
               "sling.servlet.paths=/bin/getstates",
               "sling.servlet.methods=GET",
               "sling.servlet.extensions=json"
           })
public class StateDropdownServlet extends SlingAllMethodsServlet {

    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
            throws ServletException, IOException {

        String country = request.getParameter("country");
        response.setContentType("application/json");

        JSONArray states = new JSONArray();

        if ("usa".equalsIgnoreCase(country)) {
            states.put(new JSONObject().put("text", "California").put("value", "ca"));
            states.put(new JSONObject().put("text", "Texas").put("value", "tx"));
        } else if ("canada".equalsIgnoreCase(country)) {
            states.put(new JSONObject().put("text", "Ontario").put("value", "on"));
            states.put(new JSONObject().put("text", "Quebec").put("value", "qc"));
        }

        response.getWriter().write(states.toString());
    }
}

Don't forget to add this servlet to a core bundle and deploy it.

 

3. JavaScript to Handle the Country Change

In your component's clientlib (category: cq.authoring.dialog), add this JS file, e.g., clientlibs/dialog/js/country-state.js.

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

    $(document).on("foundation-contentloaded", function() {
        var $country = $("#countrySelect");
        var $state = $("#stateSelect");

        // Listen for country changes
        $country.on("change", function() {
            var selectedCountry = $(this).val();

            if (!selectedCountry) return;

            $.ajax({
                url: "/bin/getstates?country=" + selectedCountry,
                dataType: "json",
                success: function(data) {
                    // Clear existing options
                    $state[0].items.clear();

                    // Add new options
                    data.forEach(function(item) {
                        $state[0].items.add({
                            content: {
                                textContent: item.text
                            },
                            value: item.value
                        });
                    });
                }
            });
        });
    });
})(document, Granite.$);

4. Clientlib Configuration

Create a clientlibs/dialog folder with a cq:ClientLibraryFolder node:

categories = ["cq.authoring.dialog"]
dependencies = ["granite.jquery"]

And include your country-state.js inside js.txt.

Regards,
Amit

Avatar

Community Advisor

Hi, please refer:
https://sadyrifat.medium.com/show-hide-aem-cq-dialog-fields-based-on-select-field-selection-a-compre...

for each value (country) in the dropdown, you can show a different dropdown which will have the list of states associated to that country.

Thanks