Form-control behavior
The controller manages named inputs, selects, and textareas. Use this page when choosing field names or interpreting getValue() and setValue() results.
Reading values
Section titled “Reading values”| Control | getValue(name) |
|---|---|
| Text-like input or textarea | First matching value as string. |
| Single select | Selected value as string. |
| Multiple select | Selected option values as string[]. |
| Radio group | Checked value as string; '' when none is checked. |
| One checkbox | Checked value as string; '' when unchecked. |
| Checkbox group with a bare name | One checked value collapses to string; several become string[]; none becomes ''. |
| File input | Selected files as File[], including an empty array. |
| Missing name | undefined. |
The name="field[]" convention
Section titled “The name="field[]" convention”The [] suffix explicitly requests collection behavior when more than one matching field exists.
| Group | Result |
|---|---|
| Repeated text, select, or textarea | One string per control in DOM order. |
| Repeated checkboxes | Always string[] of checked values, including []. |
| Radio group | Remains scalar because a radio group represents one choice. |
| Repeated file inputs | Concatenated File[] from every input. |
A single field ending in [] |
Uses its normal scalar/control-specific behavior. |
Bare repeated text fields keep legacy behavior: getValue() reads the first and setValue() broadcasts one value to every match.
Writing values
Section titled “Writing values”| Control | setValue(name, value) |
|---|---|
| Text-like input or textarea | Writes String(value); null/undefined become ''. |
| Multiple select | Selects options matching a scalar or any item in an array. |
| Checkbox group | An array checks matching values; a scalar checks only its matching value. |
| Radio group | Checks the option matching the scalar string. Arrays are stringified and should not be used. |
| File input | Browsers forbid assigning files. Pass [] only to clear the field. |
Repeated name="field[]" text/select/textarea |
Distributes array items by index; clears surplus controls and drops surplus values. |
| Missing name | No-op; returns the controller. |
After writing, setValue() dispatches one bubbling input event from the first matching field. That single event enters error clearing, validation, watchers, subscribers, visual sync, and auto-submit once.
DOM lookup and external controls
Section titled “DOM lookup and external controls”getField(name) returns one element, an array for repeated names, or null. It uses form.elements, so controls associated through form="id" are included even when they sit outside the form subtree. Delegated input handling also listens for those controls.
The form’s MutationObserver watches only the form subtree. Adding an external form="id" control does not itself create a mutation notification, but its later input events are handled normally.
Serialization differs from field reads
Section titled “Serialization differs from field reads”getData() and parseFormData() use native FormData semantics:
- Disabled controls and unnamed controls are omitted.
- Repeated names become arrays in the plain
dataobject. - Empty file placeholders are removed from both outputs.
- A successful named submit button is included only when supplied as the submitter.
Filevalues remain files and are not directly JSON-serializable.- Reserved keys such as
constructorand__proto__are safe own properties.
getState().values is controller-oriented rather than a submission payload: it includes tracked validator names and normalized field reads. Use getData() for network submission.
Validation notes
Section titled “Validation notes”- Arrays use item count for
minLengthandmaxLength. - Required checkbox groups pass when at least one value is selected.
- A multiple select returns an array and follows the same array rules.
- File arrays pass
requiredwhen at least one file exists. - Pattern validation converts arrays to a comma-joined string; file entries use filenames.
numeric,min, andmaxvalidate non-empty scalar signed decimal strings; bounds are inclusive.
Per-control validation with each
Section titled “Per-control validation with each”Repeated controls can share one exact name and still receive independent rules:
form('signers', { validators: { 'signer_email[]': { minLength: 1, each: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ } } }})Group rules such as the outer minLength use the aggregate value. Rules inside each use each concrete field value. On controller validation, only the members that fail each receive css-error and aria-invalid; group-level and manual errors still mark every control in the named group. Public errors stay flat as FormErrors[name]: string[], with member messages appended in DOM order.