Components
customdatatable
A read-only data table that displays rows fetched from an integration gateway endpoint or populated from the form model via expressions.
A read-only data table that displays rows fetched from an integration gateway endpoint (asyncData: true) or populated from the form model via expressions/populates (dataType: "STATIC").
Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.
When to use
- Displaying tabular results from an integration gateway endpoint on form init (e.g. transaction history, parcel records, shareholder lists fetched via
endpointCode) - Rendering rows already populated into the form model by an upstream lookup (
customgenericdatafetch+populates) for read-only display - Showing server-paginated, searchable, sortable tables where row click selection is not required
When NOT to use
Instead of customdatatable, use… | For… |
|---|---|
customdatasettable | Data already in the form model (set via expressions) or a direct non-gateway URL fetch; or when row click selection is needed |
customitemselectiontable | Picking items row-by-row with explicit Add/Remove action buttons and a paired selected-items table |
customeditabletable | Inline-editable grids where each cell is a typed input (text, number, date, select) |
customdropdowndatafetchpaginated | Picking a single value from a large paginated integration-driven list |
Props
| Prop | Type | Required? | Description |
|---|---|---|---|
columns | array | required | Column definitions — each entry: { "key": "fieldInRow", "title": "Display Label" } |
dataType | "STATIC" | "DYNAMIC" | optional | "STATIC" uses props.items (set via expression); "DYNAMIC" is unused for async fetches (use asyncData instead). |
items | array | optional | Row data array; set via expressions when dataType is "STATIC". Never hardcode. |
asyncData | boolean | optional | If true, fetches rows from dataset on component init; default false. |
dataset | object | required when asyncData: true | API fetch configuration (see below). |
selectable | boolean | optional | Allow row selection; set false for pure display. Default true. |
isSelectable | boolean | optional | Alternative selection flag used alongside selectable; set false for read-only. |
minSelection | number | optional | Minimum rows that must be selected. |
maxSelection | number | optional | Maximum selectable rows. |
searchable | boolean | optional | Show search input; default true. |
isSearchVisible | boolean | optional | Alternative visibility flag for the search input. |
searchPlaceHolder | string | optional | Placeholder text for the search box; default "Search". |
searchKeywordKey | string | conditional | API request param key for the search term (required when searching with asyncData: true). |
isSortVisible | boolean | optional | Show sort controls. |
sortKey | string | optional | API param key for the sort column. |
sortDirectionKey | string | optional | API param key for sort direction; if omitted, direction is appended to sortKey. |
paginationVisible | boolean | optional | Show pagination controls. |
pageSize | number | optional | Rows per page; default 10. |
pageSizeOptions | array | optional | Available page size choices; default [10, 50, 100]. |
pageSizeKey | string | optional | Key name for page size in API request params. |
currentPageKey | string | optional | Key name for current page number in API request params. |
zeroBasedPageIndex | boolean | optional | If true, page index sent to API starts at 0; default false. |
emptyMessage | string | optional | Message when no rows exist; default "No Records". |
noDataMessage | string | optional | Alternative empty-state message key (mirrors emptyMessage). |
title | string | optional | Table heading rendered above the table. |
customTableClass | string | optional | Extra CSS class applied to the table element. |
minimalPagination | boolean | optional | Show simplified pagination controls; default false. |
hideFromPreview | boolean | optional | Exclude from the application preview/summary page. |
dataset object (when asyncData: true)
| Field | Type | Required? | Description |
|---|---|---|---|
url | string | required | API endpoint URL (usually /integration/v1/fetch/sync). |
endpointCode | string | conditional | Gateway endpoint code; triggers integration fetch sync POST. |
dataField | string | optional | Dot-notation path to the rows array in the API response. |
httpMethod | "POST" | "GET" | optional | HTTP method; defaults to GET when omitted. |
useBaseUrl | boolean | optional | Prepend API gateway base URL to dataset.url. |
params | object | optional | Additional query/body params sent with every request. |
headers | object | optional | Custom HTTP headers. |
collectionSizeKey | string | optional | Dot-notation path to total record count in response (for server-side pagination). |
Examples
Static rows from the form model
The most common pattern: rows populated by an upstream lookup (expressions/populates) and displayed read-only.
{
"key": "PREVIOUS_FOUNDERS",
"type": "customdatatable",
"props": {
"label": "Previous Shareholders",
"dataType": "STATIC",
"selectable": false,
"isSelectable": false,
"searchable": false,
"isSearchVisible": false,
"isSortVisible": false,
"paginationVisible": false,
"noDataMessage": "No shareholders found",
"columns": [
{ "key": "firstName", "title": "First Name" },
{ "key": "lastName", "title": "Last Name" }
]
},
"expressions": {
"props.items": "model?.FETCHED_SHAREHOLDERS"
}
}Async API fetch with pagination and search
Server-paginated fetch from the integration gateway with searching, sorting, and zero-based pagination.
{
"key": "PROPERTY_HISTORY",
"type": "customdatatable",
"props": {
"label": "Property Transaction History",
"selectable": false,
"isSelectable": false,
"searchable": true,
"isSearchVisible": true,
"searchPlaceHolder": "Search transactions",
"searchKeywordKey": "keyword",
"isSortVisible": true,
"paginationVisible": true,
"asyncData": true,
"dataset": {
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "GET_PROPERTY_TRANSACTIONS",
"dataField": "data.transactions",
"httpMethod": "POST",
"params": {
"propertyType": "RESIDENTIAL"
},
"collectionSizeKey": "data.totalElements"
},
"columns": [
{ "key": "transactionDate", "title": "Date" },
{ "key": "description", "title": "Description" },
{ "key": "amount", "title": "Amount (RWF)" },
{ "key": "status", "title": "Status" }
],
"pageSize": 10,
"pageSizeOptions": [10, 50, 100],
"pageSizeKey": "size",
"currentPageKey": "page",
"zeroBasedPageIndex": true,
"sortKey": "sort",
"sortDirectionKey": "direction",
"emptyMessage": "No transaction history found",
"title": "Transaction History",
"hideFromPreview": true
}
}Common mistakes
- Using
{ "field": "...", "header": "..." }for column definitions — the correct format is{ "key": "...", "title": "..." }. - Setting
asyncData: truebut omittingdataset— the component skips the fetch and renders an empty table. - Omitting both
selectable: falseandisSelectable: falseon a display-only table — the default forselectableistrue, making rows clickable when that is not intended. - Setting
searchable: truewithasyncData: truebut omittingsearchKeywordKey— the search box appears but the API receives no keyword param. - Using
zeroBasedPageIndex: false(or omitting it) when the API expects 0-based pages — the first page of results is skipped. - Putting
collectionSizeKeydirectly onpropsinstead of insideprops.dataset— the total count is never read. - Setting
props.itemsas a static array in the config instead of viaexpressions—itemsmust be populated at runtime from the model.
Checklist
-
keyisUPPER_SNAKE_CASEand unique across the entire form -
props.labelis present, unique, and correctly cased -
columnsuse the{ "key": "...", "title": "..." }shape - Display-only tables set both
selectable: falseandisSelectable: false -
asyncData: trueis paired with a completedatasetblock (url,endpointCode,dataField) -
searchable: truewithasyncData: trueincludessearchKeywordKey -
props.itemsis set viaexpressions, never hardcoded -
zeroBasedPageIndexmatches what the API expects -
collectionSizeKeylives insidedataset, not on the rootprops - Not using
customdatatablewhen row selection or direct non-gateway URLs are needed (see "When NOT to use")