Cascading Dropdown to 3 levels

Certified Senior Developer

I am trying to implement 3 level cascading dropdown fields. 'State' dropdown field will have choices based on 'country' selected and 'City' dropdown field will have choices based on 'State' selected. I have created choices for 'States' field using choose() function. But not sure how to create choices for 'City' field. It would be great if anyone can respond to this.

Note: I am using localVariables for all the data. Not querying to DB. This is only for testing purpose.

  Discussion posts and replies are publicly visible

  • 0
    Certified Lead Developer

    How you do this largely depends on where and how you're sourcing your state / city data.  Are you querying them from a DB on demand?  Or all up front?  Or getting them from somewhere else?

    As a personal preference I typically avoid the choose() function as it has particular drawbacks that hinder its use (like the near impossibility of specifying a safe/default condition without bending over backwards).  But I can't tell what to recommend you should try without seeing perhaps some sample code.

    If you could post either your interface code or a representative example here that  would help (please use the "Insert --> Insert Code" tool to create a code box which will retain formatting and indentation, as well as preventing the thread from becoming impossible to read).

  • 0
    Certified Lead Developer

    I have to agree with Mike quite strongly on the limitations of the choose function here.  The difficulty is that with choose, you have to know beforehand how many options you will have, and always have to have exactly that many options, or you invite aberrant behavior at the very best.

    The goal is to make choiceLabels and choiceValues.  You could create a helper rule that just outputs a list of cities given a state input.  That could actually run on a simple choose function, but return lists of Text.  That gets you labels.  Then call the same rule, but a!forEach(items: rule!yourHelperRule(ri!state), expression: fv!index) to get the choiceValues.  Maybe that would work?

  • Hi Sudip

    The key to this is the data model. You want to create data sets that can be related. There will be three sets of data:

    • Countries, with a key and a name e.g. '1' and 'USA'
    • States, with a key, a foreign key relating the State to a Country, and a name e.g. '1', '1', 'Colorado'
    • Cities, with a key, and a foreign key relating the City to the State, and a name e.g. '1', '1', 'Denver'

    Here's an example:

    a!localVariables(
      local!countries: {
        {countryId: 1, countryName: "Australia"},
        {countryId: 2, countryName: "United States of America"}
      },
      local!states: {
        /* States of Australia */
        {stateId: 1, countryId: 1, stateName: "New South Wales"},
        {stateId: 2, countryId: 1, stateName: "Queensland"},
        /* States of USA */
        {stateId: 11, countryId: 2, stateName: "Alabama"},
        {stateId: 12, countryId: 2, stateName: "Alaska"},
        {stateId: 13, countryId: 2, stateName: "Arkansas"},
        {stateId: 14, countryId: 2, stateName: "California"},
        {stateId: 15, countryId: 2, stateName: "Colorado"}
      },
      local!cities: {
        /* Cities of New South Wales, Australia */
        {cityId: 1, stateId: 1, cityName: "Sydney"},
        {cityId: 2, stateId: 1, cityName: "Wollongong"},
        /* Cities of Colorado, USA */
        {cityId: 12, stateId: 15, cityName: "Boulder"},
        {cityId: 13, stateId: 15, cityName: "Denver"}
      },
      local!selectedCountry: null,
      local!selectedState: null,
      local!selectedCity: null,
      {
        a!dropdownField(
          label: "Countries",
          placeholderLabel: "---Select a Country---",
          choiceLabels: fn!index(local!countries,"countryName", null),
          choiceValues: fn!index(local!countries,"countryId",null),
          value: local!selectedCountry,
          saveInto: {
            local!selectedCountry,
            a!save(
              {local!selectedState,local!selectedCity},
              null
            )
          }
        ),
        a!dropdownField(
          showWhen: fn!not(fn!isnull(local!selectedCountry)),
          label: concat(
            "States for ",
            local!countries.countryName[
              wherecontains(
                local!selectedCountry,fn!tointeger(local!countries.countryId))
            ]
          ),
          placeholderLabel: "---Select a State ---",
          choiceLabels: local!states.stateName[wherecontains(local!selectedCountry,tointeger(local!states.countryId))],
          choiceValues: local!states.stateId[wherecontains(local!selectedCountry,tointeger(local!states.countryId))],
          value: local!selectedState,
          saveInto: {
            local!selectedState,
            a!save(
              local!selectedCity,
              null
            )
          }
        ),
        a!dropdownField(
          showWhen: fn!not(fn!isnull(local!selectedState)),
          label: concat(
            "Cities for ",
            local!states.stateName[
              wherecontains(
                local!selectedState,fn!tointeger(local!states.stateId))
            ]
          ),
          placeholderLabel: "---Select a City ---",
          choiceLabels: local!cities.cityName[wherecontains(local!selectedState, tointeger(local!cities.stateId))],
          choiceValues: local!cities.cityId[wherecontains(local!selectedState, tointeger(local!cities.stateId))],
          value: local!selectedCity,
          saveInto: {
            local!selectedCity
          }
        )
      }
    )