Components

customdropdownpaginated

A dropdown that loads options in pages from a direct backend API. Use when the list is too large to load at once but does not need to go through the integration engine.

A dropdown that loads options in pages from a direct backend API. Use when the list is too large to load at once but does not need to go through the integration engine.

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

When to use

  • The option list is too large for customdropdown (~100+ items)
  • Options come from a direct backend endpoint, not the integration engine
  • The user benefits from infinite scroll and optional server-side search

When NOT to use

Instead of customdropdownpaginated, use…For…
customdropdownSmall static option lists (~under 100 items)
customdropdowndatafetchpaginatedOptions sourced through the integration engine (requires endpointCode)
customcascadingdropdownsDependent dropdowns (e.g. province → district → sector)

Props

PropTypeRequired?Description
labelstringrequiredDisplay label; must be unique across the form.
bindLabelstringrequiredKey in each option object used as the display text.
bindValuestringrequiredKey in each option object used as the stored value. Use "" to store the whole object.
dataset.urlstringrequiredAPI endpoint that returns paginated options.
dataset.dataFieldstringrequiredDot-notation path to the array of options inside the response.
dataset.paramsobjectrequiredInitial query/body params; must include a page key set to the starting page (usually 0).
dataset.totalPagesKeystringrequiredKey in the response that holds the total page count.
dataset.currentPageKeystringrequiredKey in the response that holds the current page number.
dataset.paginationbooleanrequiredMust be true to enable the "load more" scroll trigger.
requiredbooleanrequiredAlways false. The expression controls the runtime value.
defaultRequiredbooleanrequiredtrue for required fields, false for optional ones.
dataset.httpMethodstringoptional'GET' or 'POST'. Defaults to the base dropdown setting.
dataset.headersobjectoptionalExtra HTTP headers to include.
dataset.searchingbooleanoptionalWhen true, sends a search query to the API as the user types.
dataset.searchFieldstringoptionalParam name for the search value. Defaults to bindLabel.
placeholderstringoptionalGhost text shown before a value is selected.
multiplebooleanoptionalAllows selecting multiple values.
virtualScrollbooleanoptionalEnables virtual scrolling for very long lists.
useBaseUrlbooleanoptionalPrepend the API gateway base URL to dataset.url.
minnumberoptionalMinimum number of selectable items when multiple: true.
maxnumberoptionalMaximum number of selectable items when multiple: true.
hideFieldbooleanoptionalHides the field and excludes its value from submission. Toggle via expressions["props.hideField"].

Examples

Minimal — paginated land parcels

{
  "key": "PARCEL",
  "type": "customdropdownpaginated",
  "props": {
    "label": "Land Parcel",
    "required": false,
    "defaultRequired": true,
    "bindLabel": "parcelCode",
    "bindValue": "id",
    "useBaseUrl": true,
    "dataset": {
      "url": "/admin/v1/parcels",
      "dataField": "data.content",
      "params": { "page": 0, "size": 20 },
      "totalPagesKey": "data.totalPages",
      "currentPageKey": "data.number",
      "pagination": true
    }
  },
  "expressions": {
    "props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
  },
  "validation": {
    "messages": {
      "required": "Parcel is required."
    }
  }
}

Full example — server-side search by parcel code

{
  "key": "PARCEL",
  "type": "customdropdownpaginated",
  "props": {
    "label": "Land Parcel",
    "required": false,
    "defaultRequired": true,
    "placeholder": "Search or scroll to select a parcel",
    "bindLabel": "parcelCode",
    "bindValue": "id",
    "useBaseUrl": true,
    "dataset": {
      "url": "/admin/v1/parcels",
      "dataField": "data.content",
      "httpMethod": "GET",
      "params": { "page": 0, "size": 20 },
      "totalPagesKey": "data.totalPages",
      "currentPageKey": "data.number",
      "pagination": true,
      "searching": true,
      "searchField": "parcelCode"
    }
  },
  "expressions": {
    "props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
  },
  "validation": {
    "messages": {
      "required": "Parcel is required."
    }
  }
}

Common mistakes

  • Omitting dataset.pagination: true — the "load more" trigger never fires and only the first page loads.
  • Omitting dataset.totalPagesKey or dataset.currentPageKey — pagination breaks because the component cannot track page state.
  • Not including page in dataset.params — the API receives no page parameter on the first request.
  • Using customdropdownpaginated when the list has fewer than ~100 items — use customdropdown instead; paginated adds unnecessary complexity.
  • Setting bindValue: "" when a string ID is needed — an empty bindValue stores the whole option object, not the ID.

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 > customdropdownpaginated
  • required: false is set (never true)
  • defaultRequired is set (true or false)
  • expressions["props.required"] is present with the exact required expression
  • bindLabel and bindValue match real keys in the response option objects
  • dataset.pagination: true
  • dataset.params includes a starting page value
  • dataset.totalPagesKey and dataset.currentPageKey match the response shape
  • validation.messages.required is present when the field is required

On this page