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.
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 |
| 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"]. |
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 fields
{
"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." }
}
}
]
}
}
{
"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." }
}
}
]
}
}
fieldGroup on the repeater root instead of inside fieldArray.fieldGroup — rows won't render.fieldArray entirely — the repeater renders an empty shell with no row template.fieldArray as an array ([]) instead of an object ({}) — fieldArray is a single field config object, not a list.customlabelvaluerepeater when rows come from an API — use customdynamicrepeater so auto-injection and unmodified-row filtering work correctly.customlabelvaluerepeater when uniqueness, minItems/maxItems, or conditional disabling is needed — use customrepeater instead.key is UPPER_SNAKE_CASE and unique across the entire formprops.label is present, unique, and correctly casedsections > formly-group > block > customlabelvaluerepeaterfieldArray is present and is an object (not an array)fieldArray.fieldGroup, not fieldGroupprops.required: true is set and validation.messages.required is provided