Almost every real service changes the form while the applicant types: a passport number only for foreigners, an address abroad only for people living outside Rwanda, an end date that must come after the start date. You write these rules as expressions: small JavaScript conditions in the field's expressions object, evaluated over model (all the answers).
There is no "show when" setting. You write a hide expression with the opposite condition. The SRD says "City: visible when Lives in Rwanda is No", so the field hides whenever the answer is anything else, including empty:
A field shown only when livesInRwanda is NO
{
"key": "cityOfResidence",
"type": "input",
"props": { "label": "City", "required": false, "defaultRequired": true },
"expressions": {
"hide": "model?.livesInRwanda !== 'NO'",
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
}
}
?. avoids an error while the model is still emptyhide or hideField
| Mechanism | Removes from the page | Clears the value | Use for |
|---|---|---|---|
expressions.hide |
Yes | Yes | The normal case: a hidden answer must not be submitted |
expressions["props.hideField"] |
No (only invisible) | No | A field filled silently by a data fetch, or a child of a block that already has a hide |
Gotcha · never write "required": true
A static
"required": truestays true when the field is hidden, so the applicant cannot submit and cannot see why. Every field uses the same three properties:"required": false,"defaultRequired": true(or false for optional fields) and the expression!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired. Copy the expression exactly; only defaultRequired changes.
Patterns, lengths and ranges go in props (pattern, minLength, maxLength, min, max). Every constraint needs its message in validation.messages (for example "pattern": "Use 16 digits."), with the text the SRD gives, so the applicant reads "Use 16 digits" instead of a generic error.
"The end date must be after the start date" uses the validator compareDateFields. It goes on the root of the form (the sections container), not on a date field, so all date rules sit in one place. Reference: compareDateFields.
On the root of the form
{
"type": "sections",
"validators": {
"validation": [{
"name": "compareDateFields",
"options": {
"fieldKeys": {
"contractStartDate": [{
"comparator": "before",
"fieldKey": "contractEndDate",
"message": "The end date must be after the start date."
}]
}
}
}]
}
}
Each level of the Location widget is stored with its id and its label under dotted keys: in the demo, DISTRICTCD12.id and DISTRICTCD12.label. Office routing uses the .id; certificates and notifications print the .label, read with ${.vars["key.label"]!""} (lesson B9 shows the classic empty-district bug).
practice in a change request of your own (ask your trainer for a practice service if you have none)
- Add a radio
livesInRwandawith options YES and NO, required.- Add Country and City with
"hide": "model?.livesInRwanda !== 'NO'"and the required pattern.- Add two dates and a
compareDateFieldsrule on the root.- Preview: choose YES and submit with Country empty (allowed), then NO (blocked until filled). Type an end date before the start date and read your message.
Tip · reference
Country is required and hidden with "hide": "model?.livesInRwanda !== 'NO'". The applicant answers YES. Can they submit with Country empty?
B. The expression checks field?.hide first. That is why every field uses the pattern instead of a static required.
← A6 · Form Builder II: the code editor and components · A7 · Pricing basics: free, fixed and where the money goes →