Components
customdoubleidinput
Captures and verifies two identities simultaneously as a paired unit, triggering one combined API fetch. Use for paired records like marriage lookups or joint applicants.
Captures and verifies two identities simultaneously as a paired unit, triggering one combined API fetch. Use for paired records like marriage lookups or joint applicants.
Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.
When to use
- Two identities must be verified together against a single integration endpoint (e.g. a marriage record lookup)
- The response is a combined payload that populates fields for both identities
- A single document type dropdown can serve both sub-fields
When NOT to use
Instead of customdoubleidinput, use… | For… |
|---|---|
Two customidinput fields | Independent fetches with separate result sets per identity |
customidinput | A single identity lookup |
customcrvsidinput | A CRVS (Civil Registration) lookup keyed by a single identity |
Props
| Prop | Type | Required? | Description |
|---|---|---|---|
label | string | required | Field label. Can be empty if sub-labels carry the context. |
url | string | required | Integration endpoint path — always /integration/v1/fetch/sync. |
endpointCode | string | required | Integration endpoint code that identifies the lookup service. |
allowedDocumentTypes | array | required | List of accepted document type objects — see shape below. |
required | boolean | required | Always false. The expression controls the runtime value. |
defaultRequired | boolean | required | true for required fields, false for optional ones. |
useBaseUrl | boolean | optional | Prepend the API gateway base URL to url. Set true for production endpoints. |
populates | array | optional | { valueKey, targetKey } mappings that auto-fill downstream fields from the response. |
firstIdentityLabel | string | optional | Label above the first identity sub-field. Default "First Identity". |
firstIdentityPlaceholder | string | optional | Placeholder for the first identity input. |
firstIdentityValueKey | string | optional | Response + payload key for the first document number. Default "firstDocumentNumber". |
firstIdentityDocumentTypeKey | string | optional | Payload key for the first document type. Default "firstDocumentType". |
secondIdentityLabel | string | optional | Label above the second identity sub-field. Default "Second Identity". |
secondIdentityPlaceholder | string | optional | Placeholder for the second identity input. |
secondIdentityValueKey | string | optional | Response + payload key for the second document number. Default "secondDocumentNumber". |
secondIdentityDocumentTypeKey | string | optional | Payload key for the second document type. Default "secondDocumentType". |
hideField | boolean | optional | Hides the field and excludes its value from submission. Toggle via expressions["props.hideField"]. |
allowedDocumentTypes item shape
| Key | Type | Description |
|---|---|---|
label | string | Display name shown in the document type dropdown. |
value | string | Code sent in the API payload (e.g. "NID", "PASSPORT_ID"). |
pattern | string | Regex the entered document number must match. |
maxLength | number | Maximum character count for the document number. |
minLength | number | Minimum character count for the document number. |
placeholder | string | Optional custom placeholder for this document type. |
Examples
Minimal — marriage record by NID
{
"key": "COUPLE_IDENTITY",
"type": "customdoubleidinput",
"props": {
"label": "",
"required": false,
"defaultRequired": true,
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "FGETMARRIAGERECORD",
"firstIdentityLabel": "Husband",
"secondIdentityLabel": "Wife",
"allowedDocumentTypes": [
{
"label": "National ID",
"value": "NID",
"pattern": "^\\d*$",
"maxLength": 16,
"minLength": 16
}
]
},
"expressions": {
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
},
"validation": {
"messages": {
"required": "This field is required.",
"invalidInput": "Invalid identity document."
}
}
}Full example — NID or Passport with populates
{
"key": "COUPLE_IDENTITY",
"type": "customdoubleidinput",
"props": {
"label": "",
"required": false,
"defaultRequired": true,
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "FGETMARRIAGERECORD",
"firstIdentityLabel": "Husband",
"firstIdentityPlaceholder": "Husband's Details",
"firstIdentityValueKey": "HUSBAND_DOCUMENT_NUMBER",
"firstIdentityDocumentTypeKey": "HUSBAND_DOCUMENT_TYPE",
"secondIdentityLabel": "Wife",
"secondIdentityPlaceholder": "Wife's Details",
"secondIdentityValueKey": "WIFE_DOCUMENT_NUMBER",
"secondIdentityDocumentTypeKey": "WIFE_DOCUMENT_TYPE",
"allowedDocumentTypes": [
{
"label": "National ID",
"value": "NID",
"pattern": "^\\d*$",
"maxLength": 16,
"minLength": 16
},
{
"label": "Passport",
"value": "PASSPORT_ID",
"pattern": "^[a-zA-Z0-9]*$",
"maxLength": 10,
"minLength": 5
}
],
"populates": [
{ "valueKey": "husbandDetails.surName", "targetKey": "HUSBAND_SURNAME" },
{ "valueKey": "civilStatusOfficeName", "targetKey": "OFFICE_OF_REGISTRATION" }
]
},
"expressions": {
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
},
"validation": {
"messages": {
"required": "This field is required.",
"invalidInput": "Invalid identity document."
}
}
}Common mistakes
- Setting
required: truedirectly — use therequired: false+defaultRequired: true+ expression pattern from Form Rules. - Omitting
url— the component throws at runtime; always set it to/integration/v1/fetch/sync. - Omitting
endpointCode— the component throws at runtime; always required. - Omitting
allowedDocumentTypes— the document type dropdown renders empty and the user cannot select a type. - Using
customdoubleidinputwhen the two identities trigger independent fetches with separate result sets — use twocustomidinputfields instead. - Using dot-notation
valueKey(e.g."husbandDetails.surName") without confirming the API response uses nested objects — flat keys are safer unless the response shape is verified.
Checklist
-
keyisUPPER_SNAKE_CASEand unique across the entire form -
props.labelis present (may be empty if sub-labels carry context) - Field is nested at the correct depth:
sections > formly-group > block > customdoubleidinput -
required: falseis set (nevertrue) -
defaultRequiredis set (trueorfalse) -
expressions["props.required"]is present with the exact required expression -
urlis/integration/v1/fetch/syncanduseBaseUrlistruein production -
endpointCodematches a valid integration endpoint -
allowedDocumentTypeshas at least one entry withlabel,value,pattern,minLength,maxLength - If
populatesis used, everytargetKeycorresponds to a declared field in the form -
validation.messages.requiredandvalidation.messages.invalidInputare both present