Components

customcascadingdropdowns

A multi-level cascading dropdown for hierarchical selection. Each level filters the options available in the next level based on the parent selection.

A multi-level cascading dropdown for hierarchical selection. Each level filters the options available in the next level based on the parent selection.

Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.

When to use

  • Rwanda administrative location selection (Province → District → Sector → Cell → Village)
  • Any hierarchical selection where each level depends on the parent

When NOT to use

Instead of customcascadingdropdowns, use…For…
customdropdownA flat, non-hierarchical list

Props

PropTypeRequired?Description
labelstringrequiredVisible label for the overall field group.
requiredbooleanrequiredAlways false. The expression controls the runtime value.
defaultRequiredbooleanrequiredtrue for required fields, false for optional ones.
configsarrayrequiredOrdered array of level definitions, top to bottom. See below.
locationFirstLevelstringrequiredTop-most level key (e.g. "PROVINCE").
locationLastLevelstringrequiredBottom-most level key (e.g. "VILLAGE").
hideLabelbooleanoptionalSet true to hide the component-level label (individual level labels are still shown).
placeholderstringoptionalGhost text for the first level dropdown.
hideFieldbooleanoptionalHides the entire component. Toggle via expressions["props.hideField"].

configs array — per-level definition

One entry per level, in order from top to bottom.

FieldTypeDescription
keystringModel key where this level's selection is stored (e.g. "PROVINCE").
labelstringLabel shown above this level's dropdown.
parentstring | nullkey of the parent level, or null for the root level.
parentBindKeystringThe field on the parent's selected object used to filter this level's options. Always "id" for location.
requiredbooleanWhether this level is required.
bindlabelstringKey in each option object to display. Always "label" for location.
bindvaluestringKey in each option object to store. "" stores the whole object (used for location so child levels can filter by id).
placeholderstringGhost text for this level's dropdown.
datasetobjectData source configuration. See below.

dataset object

FieldTypeDescription
urlstringAPI path to fetch options. Province uses /by-dataset-code/PROVINCE; all sub-levels use /by-details.
dataFieldstringKey in the API response containing the options array. Always "data" for location.
useBaseUrlbooleanAlways true — prepends the API gateway base URL.

Example — full Province → Village location picker

{
  "key": "FIELD_LOCATION",
  "type": "customcascadingdropdowns",
  "props": {
    "label": "Location",
    "required": false,
    "defaultRequired": true,
    "locationFirstLevel": "PROVINCE",
    "locationLastLevel": "VILLAGE",
    "hideLabel": true,
    "configs": [
      {
        "key": "PROVINCE",
        "label": "Province",
        "parent": null,
        "required": true,
        "bindlabel": "label",
        "bindvalue": "",
        "placeholder": "Select Province",
        "dataset": {
          "url": "/admin/v1/dataset-items/by-dataset-code/PROVINCE",
          "dataField": "data",
          "useBaseUrl": true
        }
      },
      {
        "key": "DISTRICT",
        "label": "District",
        "parent": "PROVINCE",
        "parentBindKey": "id",
        "required": true,
        "bindlabel": "label",
        "bindvalue": "",
        "placeholder": "Select District",
        "dataset": {
          "url": "/admin/v1/dataset-items/by-details",
          "dataField": "data",
          "useBaseUrl": true
        }
      },
      {
        "key": "SECTOR",
        "label": "Sector",
        "parent": "DISTRICT",
        "parentBindKey": "id",
        "required": true,
        "bindlabel": "label",
        "bindvalue": "",
        "placeholder": "Select Sector",
        "dataset": {
          "url": "/admin/v1/dataset-items/by-details",
          "dataField": "data",
          "useBaseUrl": true
        }
      },
      {
        "key": "CELL",
        "label": "Cell",
        "parent": "SECTOR",
        "parentBindKey": "id",
        "required": true,
        "bindlabel": "label",
        "bindvalue": "",
        "placeholder": "Select Cell",
        "dataset": {
          "url": "/admin/v1/dataset-items/by-details",
          "dataField": "data",
          "useBaseUrl": true
        }
      },
      {
        "key": "VILLAGE",
        "label": "Village",
        "parent": "CELL",
        "parentBindKey": "id",
        "required": true,
        "bindlabel": "label",
        "bindvalue": "",
        "placeholder": "Select Village",
        "dataset": {
          "url": "/admin/v1/dataset-items/by-details",
          "dataField": "data",
          "useBaseUrl": true
        }
      }
    ]
  },
  "expressions": {
    "props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
  },
  "validation": {
    "messages": {
      "required": "This field is required."
    }
  }
}

The Province level fetches from /by-dataset-code/PROVINCE. Every sub-level (District through Village) fetches from /by-details, passing the parent's selected object's id via parentBindKey: "id". bindvalue: "" stores the whole option object so the child level can read id from it.

Checklist

  • key is UPPER_SNAKE_CASE and unique across the entire form
  • props.label is present
  • Field is nested at the correct depth: sections > formly-group > block > customcascadingdropdowns
  • required: false is set (never true)
  • defaultRequired is set
  • expressions["props.required"] is present with the exact required expression
  • locationFirstLevel and locationLastLevel match the first and last key in configs
  • configs has one entry per level, in top-to-bottom order
  • Root level has parent: null; all subsequent levels have parent set to the previous level's key
  • Sub-levels (District through Village) have parentBindKey: "id"
  • bindvalue: "" on all levels (stores the whole object so child levels can filter by id)
  • Province uses /by-dataset-code/PROVINCE; all other levels use /by-details
  • validation.messages.required is present

On this page