Creating a form, field by field
Twelve real field kinds, five vetted patterns, and options that come from a capability instead of a list you maintain by hand — never a free-text validator.
A form field is generated from a table, not written by hand each time — pick a kind, and the editor shows exactly the controls that kind supports. Nothing here lets you write your own validation expression or paste in a URL; every choice comes from a closed list the platform already vetted.
The twelve kinds
Text, MultilineText, WholeNumber, FractionalNumber, Date, Time, PhoneNumber, Email, Select, LookupSelect, MultiSelect, Checkbox. (A thirteenth, FileUpload, exists in the underlying data model, but this build's server refuses it at publish — it still shows in the picker, so a flow built for a later release fails with a clear, specific error rather than a confusing one, not because it's usable today.)
Each kind turns on specific controls:
- Text gets length bounds and an optional pattern.
- WholeNumber gets numeric bounds and an implied digits-only pattern you can't override.
- PhoneNumber and Email each imply their own pattern automatically — nothing to configure, only to see confirmed.
- Select, LookupSelect and MultiSelect all require an option source; the latter two specifically expect that source to come from a capability rather than a list you type in.
There's no validator field anywhere on a form field. The rule is derived from the kind you picked plus the bounds you set, then shown to you read-only — so what a resident's browser enforces is always exactly what you can see on the panel, never a hand-written regular expression nobody else reviewed.
Where options come from
A choosing field's list is either typed in here — a short, stable table, reordered with up/down controls — or pulled live from one of your organization's own read capabilities. Pick the capability, then name which of its output fields holds the value and which holds the label a resident reads. There's no field for a URL or a header anywhere in this editor, on purpose: a capability-fed list is requests.streetCatalogue or it doesn't exist.
If one field's list depends on an earlier answer — boroughs feeding into streets, departments feeding into appointment slots — the platform reads that dependency directly off the earlier capability's own contract and shows you the resulting plan as a table: which argument, which field it's coming from, and whether it's actually resolved yet. You don't wire the relationship by hand; you check the verdict this panel already worked out.
Everything else on the panel
- Required, plus two one-clause conditions — visible-when, required-when — that read another field's answer. Nothing richer than a single comparison belongs here; a real decision belongs in the rule engine instead.
- Data class, prefill from an earlier variable (so a resident is never asked to retype what the flow already knows), and a column span for layout.
- Placeholder and help text, both translation keys, watched by the same completeness gate as every other piece of copy on the platform.