Form
@lit-material/formForm
A reactive controller that tracks the overall validity of a native <form> for a
Lit host. form.checkValidity()/reportValidity() already cover every field,
native inputs and form associated custom elements alike, and every lit-material form
component (Checkbox, Radio, Switch, Slider, Text Field) supports them through
ElementInternals. What's missing is reactivity: you can't observe native form
validity without your own event listeners, for example to enable or disable a submit button as the
user types instead of finding out only when they click it. FormController is that
listener, set up once.
Install
npm install @lit-material/form
Usage
import { FormController } from "@lit-material/form";
import { LitElement, html } from "lit";
import { customElement, query } from "lit/decorators.js";
import "@lit-material/text-field";
import "@lit-material/button";
@customElement("signup-form")
class SignupForm extends LitElement {
@query("form") private readonly formEl?: HTMLFormElement;
private readonly form = new FormController(this, () => this.formEl);
override render() {
return html`
<form @submit=${this.handleSubmit}>
<lit-material-text-field name="email" label="Email" required type="email"></lit-material-text-field>
<lit-material-button type="submit" ?disabled=${!this.form.valid}>Sign up</lit-material-button>
</form>
`;
}
private handleSubmit(event: SubmitEvent): void {
if (!this.form.reportValidity()) event.preventDefault();
}
}
API
new FormController(host, getForm): getForm: () => HTMLFormElement | null |
undefined is called when needed, not once at construction, so a @query("form")
reference that's still undefined during the host's first hostConnected()
resolves once that render completes.
-
valid: boolean: the form's current overall validity. Startstrueuntil the controller's first successful check. -
checkValidity()/reportValidity(): thin wrappers around the native methods that also updatevalidand render the host again if it changed.reportValidity()additionally shows native validation UI for the first invalid field. refresh(): callsgetForm()again and attaches to its result, for a form reference that can change.
Listens for input/change, and for the invalid event in the
capture phase. invalid fires once per invalid field from
checkValidity()/reportValidity() themselves or a submit attempt, and it
never bubbles, so a capture listener on an ancestor is the only way to observe it. The
invalid handler never calls checkValidity()/reportValidity()
again: those methods are what dispatch invalid, so calling them would loop forever while
any field stays invalid. The handler records false directly instead.