customlabelvaluerepeater
A simple user-driven repeater where each row is a small label/value template. Use when the applicant manually adds and removes rows.
A simple user-driven repeater where each row is a small label/value template. Use when the applicant manually adds and removes rows.
Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.
Extends Formly's FieldArrayType. Each row is an instance of the field template defined in fieldArray. The user adds rows with the add-row button and removes them per-row.
When to use
- Beneficiaries with a name and relationship
- Multiple phone numbers, alternative email addresses
- Lists of references, witnesses, or contacts
- Small variable-length structured data the user types in directly
When NOT to use
Instead of customlabelvaluerepeater, use… | For… |
|---|---|
customrepeater | Larger row templates that need minItems / maxItems, uniqueness validators, or hideLabel |
customexpansionrepeater | Long row templates that should collapse into expandable panels |
customdynamicrepeater | Rows that come from an API — the user does not control the row count |
Props
| Prop | Type | Required? | Description |
|---|---|---|---|
label | string | required | Rendered as the fieldset <legend> — the heading for the repeater. |
description | string | optional | Rendered as a <p> below the legend. |
required | boolean | optional | Marks the repeater as required (asterisk on label). |
addText | string | optional | Label on the add-row button; defaults to a generic add label if omitted. |
hideField | boolean | optional | Hides the field and excludes its value from submission. Toggle via expressions["props.hideField"]. |
Sub-field placement
Fields live inside fieldArray.fieldGroup, not inside fieldGroup at the repeater level. fieldArray is a single field-config object, not an array.
customlabelvaluerepeater
└── fieldArray
└── fieldGroup ← sub-fields live here
└── leaf fieldsExamples
Minimal — beneficiaries
{
"key": "BENEFICIARIES",
"type": "customlabelvaluerepeater",
"props": {
"label": "Beneficiaries",
"addText": "Add Beneficiary"
},
"fieldArray": {
"fieldGroup": [
{
"key": "FULL_NAME",
"type": "input",
"props": {
"label": "Full Name",
"required": true
},
"validation": {
"messages": { "required": "Full name is required." }
}
},
{
"key": "RELATIONSHIP",
"type": "customdropdown",
"props": {
"label": "Relationship",
"required": true,
"bindLabel": "label",
"bindValue": "value",
"options": [
{ "label": "Spouse", "value": "SPOUSE" },
{ "label": "Child", "value": "CHILD" },
{ "label": "Parent", "value": "PARENT" }
]
},
"validation": {
"messages": { "required": "Relationship is required." }
}
}
]
}
}Full example — shareholders with NID lookup
{
"key": "SHAREHOLDERS",
"type": "customlabelvaluerepeater",
"props": {
"label": "Shareholders",
"description": "Add all shareholders with their details.",
"required": true,
"addText": "Add Shareholder"
},
"validation": {
"messages": {
"required": "At least one shareholder is required."
}
},
"fieldArray": {
"fieldGroup": [
{
"key": "SHAREHOLDER_NID",
"type": "customidinput",
"props": {
"label": "Shareholder National ID",
"required": true,
"idType": "NID",
"nidType": "NATIONAL_ID",
"endpointCode": "NIDAGETIDINFO",
"populates": [
{ "valueKey": "forename", "targetKey": "SHAREHOLDER_FIRST_NAME" },
{ "valueKey": "surname", "targetKey": "SHAREHOLDER_LAST_NAME" },
{ "valueKey": "dateOfBirth", "targetKey": "SHAREHOLDER_DOB" }
]
},
"validation": {
"messages": {
"required": "Shareholder National ID is required.",
"invalidNidInput": "Enter a valid 16-digit Rwandan National ID."
}
}
},
{
"key": "SHAREHOLDER_FIRST_NAME",
"type": "input",
"props": {
"label": "First Name",
"required": true,
"readonly": true
},
"validation": {
"messages": { "required": "First name is required." }
}
},
{
"key": "SHAREHOLDER_LAST_NAME",
"type": "input",
"props": {
"label": "Last Name",
"required": true,
"readonly": true
},
"validation": {
"messages": { "required": "Last name is required." }
}
},
{
"key": "SHARE_PERCENTAGE",
"type": "customcurrencyformatinput",
"props": {
"label": "Share Percentage (%)",
"required": true
},
"validation": {
"messages": { "required": "Share percentage is required." }
}
}
]
}
}Common mistakes
- Placing fields directly inside
fieldGroupon the repeater root instead of insidefieldArray.fieldGroup— rows won't render. - Omitting
fieldArrayentirely — the repeater renders an empty shell with no row template. - Using
fieldArrayas an array ([]) instead of an object ({}) —fieldArrayis a single field config object, not a list. - Using
customlabelvaluerepeaterwhen rows come from an API — usecustomdynamicrepeaterso auto-injection and unmodified-row filtering work correctly. - Reaching for
customlabelvaluerepeaterwhen uniqueness,minItems/maxItems, or conditional disabling is needed — usecustomrepeaterinstead.
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 > customlabelvaluerepeater -
fieldArrayis present and is an object (not an array) - Sub-fields live inside
fieldArray.fieldGroup, notfieldGroup - If the repeater is required,
props.required: trueis set andvalidation.messages.requiredis provided - No nested repeaters
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).
custommultiselectdatafetch
A search-filter form plus results table with checkbox selection, driven by an integration engine endpoint. Use when the user must search a dynamic dataset and pick one or more results.