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… |
|---|---|
customdropdown | Small static option lists (~under 100 items) |
customdropdowndatafetchpaginated | Options sourced through the integration engine (requires endpointCode) |
customcascadingdropdowns | Dependent dropdowns (e.g. province → district → sector) |
Props
| Prop | Type | Required? | Description |
|---|---|---|---|
label | string | required | Display label; must be unique across the form. |
bindLabel | string | required | Key in each option object used as the display text. |
bindValue | string | required | Key in each option object used as the stored value. Use "" to store the whole object. |
dataset.url | string | required | API endpoint that returns paginated options. |
dataset.dataField | string | required | Dot-notation path to the array of options inside the response. |
dataset.params | object | required | Initial query/body params; must include a page key set to the starting page (usually 0). |
dataset.totalPagesKey | string | required | Key in the response that holds the total page count. |
dataset.currentPageKey | string | required | Key in the response that holds the current page number. |
dataset.pagination | boolean | required | Must be true to enable the "load more" scroll trigger. |
required | boolean | required | Always false. The expression controls the runtime value. |
defaultRequired | boolean | required | true for required fields, false for optional ones. |
dataset.httpMethod | string | optional | 'GET' or 'POST'. Defaults to the base dropdown setting. |
dataset.headers | object | optional | Extra HTTP headers to include. |
dataset.searching | boolean | optional | When true, sends a search query to the API as the user types. |
dataset.searchField | string | optional | Param name for the search value. Defaults to bindLabel. |
placeholder | string | optional | Ghost text shown before a value is selected. |
multiple | boolean | optional | Allows selecting multiple values. |
virtualScroll | boolean | optional | Enables virtual scrolling for very long lists. |
useBaseUrl | boolean | optional | Prepend the API gateway base URL to dataset.url. |
min | number | optional | Minimum number of selectable items when multiple: true. |
max | number | optional | Maximum number of selectable items when multiple: true. |
hideField | boolean | optional | Hides 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.totalPagesKeyordataset.currentPageKey— pagination breaks because the component cannot track page state. - Not including
pageindataset.params— the API receives no page parameter on the first request. - Using
customdropdownpaginatedwhen the list has fewer than ~100 items — usecustomdropdowninstead; paginated adds unnecessary complexity. - Setting
bindValue: ""when a string ID is needed — an emptybindValuestores the whole option object, not the ID.
Checklist
-
keyisUPPER_SNAKE_CASEand unique across the entire form -
props.labelis present, unique, and correctly cased - Field is nested at the correct depth:
sections > formly-group > block > customdropdownpaginated -
required: falseis set (nevertrue) -
defaultRequiredis set (trueorfalse) -
expressions["props.required"]is present with the exact required expression -
bindLabelandbindValuematch real keys in the response option objects -
dataset.pagination: true -
dataset.paramsincludes a startingpagevalue -
dataset.totalPagesKeyanddataset.currentPageKeymatch the response shape -
validation.messages.requiredis present when the field is required
customdropdowndatafetchpaginated
A paginated dropdown that fetches options through the integration engine, optionally with dependency-driven re-fetching when other form fields change.
customdynamicrepeater
An API-driven repeater that creates one row per item returned from a fetch. The user fills in the visible fields per row but does not control row count.