Components

customdropdown

A single-select or multi-select dropdown. Supports static option lists, admin dataset endpoints, and integration endpoints.

A single-select or multi-select dropdown. Supports static option lists, admin dataset endpoints, and integration endpoints.

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

When to use

  • Selecting from a known, bounded list of values (citizenship, status, category, nationality)
  • Static options or API-driven options from an admin dataset or integration endpoint
  • Single or multiple selection

When NOT to use

Instead of customdropdown, use…For…
customdropdownpaginatedLarge option lists that need server-side pagination with scroll-to-load
customdropdowndatafetchpaginatedAPI-driven options with pagination
customcascadingdropdownsProvince → District → Sector → Cell → Village and other hierarchical selection
radio2–4 mutually exclusive options that benefit from always being visible
selectNever — select is the plain Formly fallback and is not used in production forms

Props

PropTypeRequired?Description
labelstringrequiredVisible label. Title Case for nouns, Sentence case for questions. Must be unique across the form.
requiredbooleanrequiredAlways false. The expression controls the runtime value.
defaultRequiredbooleanrequiredtrue for required fields, false for optional ones.
bindLabelstringrequiredKey in each option object to display (e.g. "label", "name").
bindValuestringrequiredKey in each option object to store. Use "" to store the whole object.
optionsarrayrequired if no datasetStatic list of option objects. Typically { label, value }.
datasetobjectrequired if no optionsAPI data source. Use for options loaded at runtime.
subTypestringoptional"CUSTOM_DROPDOWN_DATASET" for admin dataset endpoints; "CUSTOM_DROPDOWN_DATAFETCH" for integration endpoints.
multiplebooleanoptionaltrue to allow multiple selections.
placeholderstringoptionalGhost text shown before a selection is made.
virtualScrollbooleanoptionalEnable virtual scrolling for large option lists.
alwaysEnabledbooleanoptionalPrevents the field being disabled by computed expressions.
tooltipstringoptionalHelp text shown as an icon tooltip next to the label.
hintstringoptionalHelp text rendered below the field, always visible.
hideFieldbooleanoptionalHides the field and excludes its value. Toggle via expressions["props.hideField"].

dataset object

FieldTypeDescription
urlstringAPI endpoint path.
dataFieldstringKey in the response body that contains the items array (e.g. "data", "data.content").
useBaseUrlbooleanAlways true for production endpoints.
httpMethodstringDefault GET. Set "POST" for integration endpoints (/integration/v1/fetch/sync).
paramsobjectQuery or body parameters sent to the API.

Examples

Static options

{
  "key": "MARITAL_STATUS",
  "type": "customdropdown",
  "props": {
    "label": "Marital Status",
    "placeholder": "Select marital status",
    "required": false,
    "defaultRequired": true,
    "bindLabel": "label",
    "bindValue": "value",
    "options": [
      { "label": "Single", "value": "SINGLE" },
      { "label": "Married", "value": "MARRIED" },
      { "label": "Divorced", "value": "DIVORCED" },
      { "label": "Widowed", "value": "WIDOWED" }
    ]
  },
  "expressions": {
    "props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
  },
  "validation": {
    "messages": {
      "required": "This field is required."
    }
  }
}

Admin dataset endpoint

Use subType: "CUSTOM_DROPDOWN_DATASET" and dataset.url pointing to /admin/v1/dataset-items/by-dataset-code/<CODE>. Set bindValue: "" to store the whole object when items are not a simple { label, value } pair.

{
  "key": "COUNTRY_OF_NATIONALITY",
  "type": "customdropdown",
  "props": {
    "label": "Country of Nationality",
    "placeholder": "Select country",
    "required": false,
    "defaultRequired": true,
    "bindLabel": "label",
    "bindValue": "",
    "subType": "CUSTOM_DROPDOWN_DATASET",
    "useBaseUrl": true,
    "dataset": {
      "url": "/admin/v1/dataset-items/by-dataset-code/COUNTRY",
      "dataField": "data"
    }
  },
  "expressions": {
    "props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
  },
  "validation": {
    "messages": {
      "required": "This field is required."
    }
  }
}

Integration endpoint

Use subType: "CUSTOM_DROPDOWN_DATAFETCH", dataset.httpMethod: "POST", and dataset.url: "/integration/v1/fetch/sync".

{
  "key": "COUNTRY_OF_STUDY",
  "type": "customdropdown",
  "props": {
    "label": "Country of Study",
    "placeholder": "Select country",
    "required": false,
    "defaultRequired": true,
    "bindLabel": "name",
    "bindValue": "",
    "subType": "CUSTOM_DROPDOWN_DATAFETCH",
    "useBaseUrl": true,
    "virtualScroll": true,
    "dataset": {
      "url": "/integration/v1/fetch/sync",
      "dataField": "data",
      "httpMethod": "POST"
    }
  },
  "expressions": {
    "props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
  },
  "validation": {
    "messages": {
      "required": "This field is required."
    }
  }
}

Common mistakes

  • Omitting bindLabel or bindValue — dropdown items render blank; both are always required.
  • Setting bindValue: "value" on a dataset-driven dropdown where items are not { label, value } — use "" to store the whole object when the item shape varies.
  • Providing neither options nor dataset — the dropdown is always empty.
  • Setting dataset.dataField to a key that doesn't exist in the API response — the dropdown silently renders empty.
  • Using a GET request against /integration/v1/fetch/sync — integration endpoints require dataset.httpMethod: "POST".
  • Using customdropdown for province/district/sector hierarchies — use customcascadingdropdowns instead.

Checklist

  • key is UPPER_SNAKE_CASE and unique across the entire form
  • props.label is present, unique, and correctly cased
  • Field is nested at the correct depth: sections > formly-group > block > customdropdown
  • required: false is set (never true)
  • defaultRequired is set
  • expressions["props.required"] is present with the exact required expression
  • bindLabel and bindValue are set
  • Exactly one of options or dataset is provided
  • For dataset-driven: subType is set and dataset.httpMethod: "POST" is set for integration endpoints
  • validation.messages.required is present

On this page