Forms
form()#
schema-based with validation rules, dirty/valid tracking, submit with error mapping
wireForm()#
auto-wire existing <form> elements, multi-step wizards, a11y (aria-invalid, aria-describedby)
Files in forms#
File values, maxSize('2MB') / mime(['image/*']) / maxFiles(n), f.formData(), f.preview(key)
Forms#
submitting, submitCount, submitError, result, errors.$form; server errors mapped from non_field_errors/detail/RFC 9457 arrays; double-submit guard
Leaving, drafts, wizards and accessible errors#
f.guard(), draft() and wizard() cover leaving, drafts and multi-step forms, and errors are accessible without a chorus of live regions: one announcement, aria-errormessage, a GOV.UK-style f.summary(). Forms grow (fieldArray, addField, observe), an HTML 422 from Rails or Django is morphed onto the live form with its errors harvested from the markup, and formnovalidate / intent buttons work exactly like they do without JavaScript. Forms separate truth from display: f.issues and f.valid are always right, f.errors is what the user sees, f.canSubmit drives the button, bind:field=${f.field('email')} wires touched / aria / :user-invalid in one attribute, and submit is a status machine with abort, a pending submitter and PRG through the router.
Two-way binding to derived values#
lens(get, set) and bind:value=${[get, set]} bind two-way to any derived or nested value, signals({ … }) names signals after their keys, and the engine warns when ${count.value} is dropped into html`` as a one-time snapshot (E048).