A search-filter form plus results table with checkbox selection, driven by an integration engine endpoint. The user fills filter inputs, clicks search, and selects one or more rows; the field value is an array of full row objects for every checked row.
Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.
- Searching a large dynamic dataset via filter inputs (e.g. "find students by name and school code")
- Multi-row selection where each row carries multiple data points the form needs to keep
- API-driven pickers with optional pagination
- Selection summaries showing counts, sums, or first-row metadata
Instead of custommultiselectdatafetch, use… |
For… |
customdropdown |
Small static lists, or single-select API-driven dropdowns |
customgenericdatafetch |
A single input that triggers a lookup and populates other fields (no row table, no multi-pick) |
customdropdownpaginated |
Paginated dropdowns where the user selects exactly one option from the list |
This is the multi-select sibling of the data-fetch family: instead of one input → one populated record, the user runs a search and picks multiple rows.
- The component renders one or more search-filter input fields above a results table.
- The user fills in the filters and clicks the search button.
- The component POSTs to
props.url with this shape:{
"endpointCode": "YOUR_ENDPOINT_CODE",
"payload": { "filterKey": "filterValue" }
}
- Results populate the table; each row has a checkbox.
- The field value is an array of full row objects for every checked row.
| Prop |
Type |
Required? |
Description |
label |
string |
required |
Display label; must be unique across the entire form. |
url |
string |
required |
Integration engine endpoint path (e.g. /integration/v1/data-fetch). |
endpointCode |
string |
required |
Endpoint code sent in every POST payload. |
inputFields |
InputFieldConfig[] |
required |
Search-filter inputs shown above the table. |
columns |
MultiSelectTableColumn[] |
required |
Column definitions for the results table. |
dataPath |
string |
required |
Dot-notation path to the results array in the API response (e.g. "data.content"). |
¶ Selection and data extraction
| Prop |
Type |
Required? |
Description |
selectionKey |
string |
optional |
Key in each row used to track selection identity; defaults to "id". |
useBaseUrl |
boolean |
optional |
Prepend the API gateway base URL; set true for integration engine calls. |
paginationConfig |
PaginationConfig |
optional |
Enables paginated results; omit when the API returns all results at once. |
| Prop |
Type |
Required? |
Description |
tableTitle |
string |
optional |
Heading displayed above the results table; defaults to "Results". |
tableDescription |
string |
optional |
Sub-heading below the title; defaults to "Please select items before continuing.". |
emptyMessage |
string |
optional |
Message shown when the search returns no results; defaults to "No results found.". |
validateButtonLabel |
string |
optional |
Label for the search trigger button; defaults to "Validate". |
| Prop |
Type |
Required? |
Description |
useMultiplePayloadKeys |
boolean |
optional |
Set true to enable payloadKeyMappings. |
payloadKeyMappings |
PayloadKeyMapping[] |
optional |
Maps form fields or static values into the request payload; only active when useMultiplePayloadKeys is true. |
¶ Preview and errors
| Prop |
Type |
Required? |
Description |
previewMetadata |
PreviewMetadataConfig[] |
optional |
Metadata entries for the selection summary panel shown after selection. |
disabled |
boolean |
optional |
Disables the entire widget. |
showGlobalError |
boolean |
optional |
Shows a global error banner on request failure. |
errorMapping |
object |
optional |
Maps integration error codes to user-facing messages. |
{
"pageablePath": "data.pageable",
"totalElementsPath": "data.totalElements",
"totalPagesPath": "data.totalPages"
}
[
{ "key": "districtCode", "sourceField": "district" },
{ "key": "year", "isStatic": true, "staticValue": 2024 }
]
| Field |
Type |
Required? |
Description |
key |
string |
required |
Parameter name sent in the POST payload. |
label |
string |
required |
Display label for the input. |
type |
string |
optional |
HTML input type: "text" (default), "number", "email". |
placeholder |
string |
optional |
Ghost text. |
hint |
string |
optional |
Helper text below the input. |
required |
boolean |
optional |
Prevents search until filled. |
pattern |
string |
optional |
Regex validation pattern. |
minLength |
number |
optional |
Minimum character count. |
maxLength |
number |
optional |
Maximum character count. |
defaultValue |
string |
optional |
Pre-filled value. |
¶ MultiSelectTableColumn shape
| Field |
Type |
Required? |
Description |
header |
string |
required |
Column heading text. |
field |
string |
required |
Top-level key in each row object. |
dataPath |
string |
optional |
Dot-notation path for nested values (e.g. "school.name"). |
formatter |
string |
optional |
"date", "currency", "array-first-name". |
| Field |
Type |
Description |
key |
string |
Unique identifier for this metadata entry. |
label |
string |
Display label. |
type |
"count" \| "sum" \| "first" \| "input" \| "static" |
How the value is computed from selected rows. |
sourceField |
string |
Row field used for sum / first computation. |
formatter |
"currency" \| "number" \| "date" |
Optional display formatter. |
prefix |
string |
Text prepended to value (e.g. "RWF "). |
suffix |
string |
Text appended to value (e.g. " items"). |
Only required is registered and functional. minSelection / maxSelection are defined in EIremboFormlyValidationTypes but not registered in formlyConfigs() — they have no effect at runtime; do not use them.
"validation": {
"messages": {
"required": "At least one item must be selected"
}
}
{
"key": "SELECTED_STUDENTS",
"type": "custommultiselectdatafetch",
"props": {
"label": "Students",
"url": "/integration/v1/data-fetch",
"endpointCode": "SEARCH_STUDENTS",
"useBaseUrl": true,
"inputFields": [{ "key": "name", "label": "Name", "required": true }],
"columns": [
{ "header": "Full Name", "field": "fullName" },
{ "header": "Student ID", "field": "studentId" }
]
},
"validation": {
"messages": {
"required": "At least one student must be selected"
}
}
}
{
"key": "SELECTED_STUDENTS",
"type": "custommultiselectdatafetch",
"props": {
"label": "Students",
"url": "/integration/v1/data-fetch",
"endpointCode": "SEARCH_STUDENTS",
"useBaseUrl": true,
"selectionKey": "studentId",
"dataPath": "data.content",
"tableTitle": "Search Results",
"tableDescription": "Select one or more students from the list below",
"emptyMessage": "No students found. Try a different name or ID.",
"validateButtonLabel": "Search",
"inputFields": [
{
"key": "name",
"label": "Name",
"type": "text",
"placeholder": "Enter student name",
"required": true,
"minLength": 2
},
{
"key": "schoolCode",
"label": "School Code",
"type": "text",
"placeholder": "e.g. SCH-001"
}
],
"columns": [
{ "header": "Full Name", "field": "fullName" },
{ "header": "Student ID", "field": "studentId" },
{ "header": "Date of Birth", "field": "dateOfBirth", "formatter": "date" },
{ "header": "School", "field": "school", "dataPath": "school.name" }
],
"paginationConfig": {
"pageablePath": "data.pageable",
"totalElementsPath": "data.totalElements",
"totalPagesPath": "data.totalPages"
},
"useMultiplePayloadKeys": true,
"payloadKeyMappings": [
{ "key": "districtCode", "sourceField": "DISTRICT" },
{ "key": "year", "isStatic": true, "staticValue": 2024 }
],
"previewMetadata": [
{
"key": "selectedCount",
"label": "Selected Students",
"type": "count",
"suffix": " items"
},
{
"key": "totalFees",
"label": "Total Fees",
"type": "sum",
"sourceField": "tuitionFee",
"formatter": "currency",
"prefix": "RWF "
}
]
},
"validation": {
"messages": {
"required": "At least one student must be selected"
}
}
}
- Omitting
endpointCode — the component makes no request; the results table stays empty with no visible error.
- Omitting
inputFields — no search form renders; the user has no way to trigger the fetch.
- Omitting
columns — the table renders with no headers or data even if the API returns results.
- Using
props.dataset.url instead of props.url — this component reads only from props.url, not props.dataset.
- Setting
selectionKey to a field absent from some rows — selection tracking breaks silently for those rows.
- Using
minSelection or maxSelection validators — defined in EIremboFormlyValidationTypes but not registered in formlyConfigs(); they have no effect at runtime.
- Providing
payloadKeyMappings without setting useMultiplePayloadKeys: true — the mappings are ignored.
- Setting
dataPath to a key whose value is not an array — the table renders nothing.
- Expecting the field value to be an array of IDs — the value is always an array of full row objects; extract the ID in the submission mapping if needed.