Components

customitemselectiontable

A table with per-row Add or Remove action buttons used to incrementally build a list of selected items, typically paired (one add table + one remove table).

A table with per-row Add or Remove action buttons used to incrementally build a list of selected items. Typically deployed as a pair: an add mode table shows available rows, a remove mode table shows what the user has picked.

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

When to use

  • Picking items from a table row-by-row using an Add/Remove action button per row — typical for selecting services, business activities, or records that require multiple visible attributes shown as columns
  • Workflows that need a paired source table (mode: "add") plus a selected-items table (mode: "remove") on the same form
  • Selection lists driven by an integration gateway endpoint with server-side pagination and search

When NOT to use

Instead of customitemselectiontable, use…For…
customdatasettableRow click selection where the chosen row becomes a single form value (no Add/Remove pairing)
customdatatablePure read-only display of integration-gateway data with no selection
customeditabletableInline-editable grids where the user types into cells row-by-row
custommultiselectdatafetchSimple dropdown-style multi-selection where only a label and value per item are needed
customdropdowndatafetchpaginatedSingle-value pick from a large paginated integration-driven list

How it works

The component operates in one of two modes set via selectionConfig.mode:

  • add mode — displays a source list; clicking the action button moves the row into selectionConfig.targetField (another field in the form that tracks selected items). Pair it with a remove mode table to show and manage selected items.
  • remove mode — displays the currently selected items (reads from its own formControl.value or props.sourceField); clicking the action button removes the row.

Data sources (priority order)

  1. API gateway — set props.url + props.endpointCode + props.dataset to fetch from the integration gateway
  2. Field reference — set props.sourceField to subscribe reactively to another form control's value
  3. Static array — set props.data directly (rarely used in production)
  4. Remove mode self — in remove mode with no other source, reads from own form control value

Props

PropTypeRequired?Description
columnsarrayrequiredColumn definitions — each entry: { "key": "fieldInRow", "title": "Display Label", "sortable": false, "width": "auto" }
selectionConfigobjectrequiredSelection behaviour configuration (see below).
urlstringrequired (API mode)API endpoint URL; always /integration/v1/fetch/sync for gateway calls. Must be on props, not inside dataset.
endpointCodestringrequired (API mode)Gateway endpoint code. Must be on props, not inside dataset.
useBaseUrlbooleanoptionalPrepend API gateway base URL to url; default true.
datasetobjectrequired (API mode)Pagination and response mapping config (see below).
payloadKeystringoptionalSingle form field key whose value is sent as payload (simple listener pattern).
useMultiplePayloadKeysbooleanoptionalIf true, use payloadKeyMappings instead of payloadKey.
payloadKeyMappingsarrayoptionalMulti-payload mappings — each: { "key": "paramName", "sourceField": "formFieldKey" } or { "key": "paramName", "isStatic": true, "staticValue": "value" }.
sourceFieldstringoptionalKey of another form control to read data from reactively (alternative to API/static).
dataarrayoptionalStatic row data array (lowest priority data source).
searchablebooleanoptionalShow search input above the table; default true.
searchPlaceHolderstringoptionalPlaceholder text for the search input; default "Search".
emptyMessagestringoptionalMessage when no rows are available; default "No items available".
titlestringoptionalHeading rendered above the table.
customTableClassstringoptionalExtra CSS class on the <table> element.
requiredbooleanoptionalAlways false. The required expression controls the runtime value.
defaultRequiredbooleanoptionalUsed with the conditional required expression pattern.
errorMappingobjectoptionalCustom API error handling config.

selectionConfig object

FieldTypeRequired?Description
mode"add" | "remove"required"add" renders an Add button per row; "remove" renders a Remove button.
targetFieldstringrequired (add)Key of the form field where added items are stored.
minItemsnumberoptionalMinimum items that must be selected; fires minSelection validation error.
maxItemsnumberoptionalMaximum items allowed; disables the Add button once reached; fires maxSelection.
preventDuplicatesbooleanoptionalPrevent adding the same row twice; default true.
actionLabelstringoptionalText for the action column header and button.
actionIconstringoptionalIcon class for the action button (e.g. "icon-plus", "icon-trash").
availableIconstringoptionalIcon shown when an item has not yet been added (add mode).
selectedIconstringoptionalIcon shown when an item is already in targetField (add mode).
removeIconstringoptionalIcon for the remove action (remove mode).
displayMode"icon-only" | "icon-text" | "text-only"optionalHow the action button renders; default "icon-text".
buttonStyle"default" | "icon-button"optional"default" outlined button; "icon-button" borderless icon link.

dataset object (API mode)

FieldTypeRequired?Description
dataFieldstringoptionalDot-notation path to the rows array in the API response.
httpMethod"POST" | "GET"optionalHTTP method; default "POST".
paramsobjectoptionalInitial request params (e.g. { "page": 0, "size": 20 }).
pageSizenumberoptionalRows per page; default 20.
pageStartnumberoptionalStarting page index sent to API; 0 for zero-based, 1 for one-based.
pageKeystringoptionalRequest param key for the page number; default "page".
sizeKeystringoptionalRequest param key for page size; default "size".
searchKeystringconditionalRequest param key for the search term (required when searchable: true with API data).
totalPagesKeystringoptionalDot-notation path to total pages in the response; default "totalPages".
totalElementsKeystringoptionalDot-notation path to total element count in the response.
headersobjectoptionalCustom HTTP headers.
searchLocalbooleanoptionalIf true, search filters client-side even when data came from API.

Validation messages

KeyWhen it fires
minSelectionSelected count is below selectionConfig.minItems
maxSelectionSelected count exceeds selectionConfig.maxItems

Examples

Minimal add-mode table

{
  "key": "AVAILABLE_SERVICES",
  "type": "customitemselectiontable",
  "props": {
    "label": "Available Services",
    "url": "/integration/v1/fetch/sync",
    "useBaseUrl": true,
    "endpointCode": "GET_SERVICES_LIST",
    "dataset": {
      "dataField": "data.services",
      "httpMethod": "POST",
      "pageSize": 10,
      "pageStart": 0,
      "pageKey": "page",
      "sizeKey": "size"
    },
    "columns": [
      { "key": "name", "title": "Service Name", "width": "70%" },
      { "key": "fee", "title": "Fee (RWF)", "width": "30%" }
    ],
    "selectionConfig": {
      "mode": "add",
      "targetField": "SELECTED_SERVICES",
      "actionIcon": "icon-plus",
      "displayMode": "icon-only"
    }
  },
  "validation": {
    "messages": {
      "minSelection": "Please select at least one service",
      "maxSelection": "Maximum services reached"
    }
  }
}

Paired add + remove tables

Source table (add mode) paired with a remove table that shows selected items.

[
  {
    "key": "AVAILABLE_BUSINESS_ACTIVITIES",
    "type": "customitemselectiontable",
    "props": {
      "label": "Select Business Activity",
      "url": "/integration/v1/fetch/sync",
      "useBaseUrl": true,
      "endpointCode": "RDB_BUSINESS_ACTIVITY_LINES_FETCH",
      "searchable": true,
      "dataset": {
        "dataField": "data.data",
        "httpMethod": "POST",
        "pageSize": 5,
        "pageStart": 1,
        "pageKey": "page",
        "sizeKey": "size",
        "searchKey": "searchKey",
        "totalPagesKey": "data.totalPages",
        "totalElementsKey": "data.totalElements",
        "params": {
          "page": 0,
          "size": 5
        }
      },
      "useMultiplePayloadKeys": true,
      "payloadKeyMappings": [
        {
          "key": "businessSector",
          "isStatic": true,
          "staticValue": "A"
        }
      ],
      "columns": [
        { "key": "desEng", "title": "Activity", "width": "70%" },
        { "key": "code", "title": "Code", "width": "30%" }
      ],
      "selectionConfig": {
        "mode": "add",
        "targetField": "SELECTED_BUSINESS_ACTIVITIES",
        "maxItems": 3,
        "actionIcon": "icon-plus",
        "selectedIcon": "icon-check",
        "displayMode": "icon-only",
        "preventDuplicates": true
      },
      "emptyMessage": "No activities found",
      "required": false,
      "defaultRequired": false
    },
    "expressions": {
      "props.hideField": "model.BUSINESS_SECTOR !== 'A'",
      "props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
    },
    "validation": {
      "messages": {
        "maxSelection": "You can select up to 3 activities only",
        "minSelection": "Please select at least one activity to proceed"
      }
    }
  },
  {
    "key": "SELECTED_BUSINESS_ACTIVITIES",
    "type": "customitemselectiontable",
    "props": {
      "label": "Selected Activities",
      "columns": [
        { "key": "desEng", "title": "Activity", "width": "70%" },
        { "key": "code", "title": "Code", "width": "30%" }
      ],
      "selectionConfig": {
        "mode": "remove",
        "actionIcon": "icon-trash",
        "displayMode": "icon-only"
      },
      "searchable": false,
      "emptyMessage": "No activities selected yet"
    }
  }
]

Common mistakes

  • Putting url and endpointCode inside dataset instead of at the top level of props — the component reads them from props directly; nesting them disables the fetch.
  • Using add mode without selectionConfig.targetField — selected items have nowhere to go; the selection is silently lost.
  • Omitting selectionConfig entirely — the component throws an error on init because selectionConfig is required.
  • Setting selectionConfig.maxItems without a maxSelection validation message — the Add button disables when the limit is reached but no error text appears.
  • Setting dataset.pageStart: 1 when the API expects 0-based pages — the first page is skipped.
  • Not pairing an add mode table with a remove mode table — the user can add items but has no way to undo selections.
  • Using searchable: true with API data but omitting dataset.searchKey — the search input appears but the keyword is never sent.

Checklist

  • key is UPPER_SNAKE_CASE and unique across the entire form
  • props.label is present, unique, and correctly cased
  • selectionConfig.mode is "add" or "remove"
  • add mode tables set selectionConfig.targetField to a valid form field key
  • A matching remove mode table exists with the same key as the targetField
  • url and endpointCode are on props, not nested inside dataset
  • dataset.pageStart matches the API's pagination indexing (0-based or 1-based)
  • searchable: true with API data includes dataset.searchKey
  • selectionConfig.maxItems has a matching validation.messages.maxSelection
  • selectionConfig.minItems has a matching validation.messages.minSelection
  • When required: required: false, defaultRequired is set, and expressions["props.required"] is present

On this page