Appearance
Form
SForm renders a native <form novalidate> and collects the fields placed anywhere inside it. On submit it checks every field, moves focus to the first invalid one and emits submit only when the whole form is valid; otherwise it emits invalid. Without a submit listener a valid form is submitted natively, so a classic form with action keeps working. The form also reports its validity through v-model and its default slot.
The rules themselves live on the fields (rules, validate-on, error): see the Validation guide. SForm adds no styles of its own and no layout: arrange the fields as you like.
Sign-up form
Press Create account with empty fields: every required field shows its error, the terms checkbox included, and focus moves to the first one. Reset clears the values and the errors.
SButton is type="button" by default, so the submit and reset buttons need type="submit" and type="reset". Pressing Enter in a text field of a form with a submit button submits it, as in any native form.
Validation modes
validate-on on the form sets when its fields check: blur (the default), input or submit. A field's own validate-on wins over the form's. The modes are described in When fields check.
The form's mode can also be set once for the app through prop defaults: defaults: { SForm: { validateOn: 'input' } }.
Validity and state
v-model on the form reports its validity:
nulluntil the form is mounted (also during server rendering), and while some field with rules has not been checked yet or an async rule is still running;truewhen every field with rules has been checked and passed;falseas soon as any field shows an error, a rule error or a manualerror.
A field without rules counts as valid unless it has a manual error, so a form with no rules at all is true right after mounting. The model is read-only for the app: a value you write into it is replaced with the actual state at once.
The default slot receives the same state and more:
| Slot prop | Meaning |
|---|---|
valid | the same value as v-model: true, false or null |
validating | whether an async rule is running in some field |
errors | the fields that show an error now, in page order (SFormError[]) |
validate | checks every field, see Methods |
resetValidation | clears the errors of every field |
SFormError is { id, name, messages }: an internal field id, the field's name (when set) and its error texts (one text per field).
Below, the button stays disabled while the form is known to be invalid, and the errors are listed above the fields:
Disable the button on valid === false, not on !valid: null means "not checked yet", and a button disabled from the start would never let the user submit and see the errors.
Methods
Through a template ref the form provides two methods (they are not listed in the API table below, which shows props, events and slots):
validate(): checks every field at once, in parallel, and resolves to{ valid, errors }, whereerrorslists the failed fields in page order. It shows the errors like a submission, but does not move focus and does not emitsubmitorinvalid;resetValidation(): clears the errors of every field and cancels a pending submission; the values stay as they are.
The same functions are also passed to the default slot.
Submission
On submit the form calls preventDefault() on the native event and checks every field. Then:
- the form is invalid: it emits
invalidwith the failed fields (SFormError[], in page order) and moves focus to the first invalid field in page order, not in the order the fields were added.no-error-focuskeeps focus where it is; - the form is valid and has a
submitlistener: it emitssubmitwith the originalSubmitEvent. Sending the data is up to the app; - the form is valid and has no
submitlistener: the browser submits it natively.
A new submission while an async rule of the previous one is still running replaces it: only the latest submission emits submit or invalid. A reset (or resetValidation()) during a pending submission cancels it, so neither event is emitted.
Native submission
Without a submit listener, a valid form is submitted with form.requestSubmit(submitter) one task after the check, where submitter is the button that was pressed. So the native submission keeps what a plain form.submit() would drop: the name and value of the pressed button, and its formaction, formmethod, formenctype and formtarget:
vue
<script setup lang="ts">
import { required } from '@smalt-ui/core'
</script>
<template>
<SForm
action="/search"
method="get"
>
<SInput
name="q"
label="Search"
:rules="[required()]"
/>
<SButton
type="submit"
name="scope"
value="all"
>
Search everywhere
</SButton>
</SForm>
</template>Attributes such as action and method go to the native <form>. One limit: a browser opens a new window (formtarget="_blank", target="_blank") only shortly after a user action. If an async rule makes the check take longer than about five seconds, the browser may block that window.
Reset
A reset button (or form.reset()) does not reset the fields natively. The form calls preventDefault(), emits reset and clears the errors of every field on the next tick. The fields are bound with v-model, so the app resets its own data in the reset handler, as in the sign-up form above; the errors are cleared after that, so the emptied fields do not show errors.
Because the native reset is prevented, inputs the app does not control are not reset either: a plain <input> without v-model, or an <input type="file">, keeps its value. Reset them in the reset handler too.
Which fields take part
A field registers in the nearest SForm when it mounts and leaves it when it unmounts, so fields under v-if join and leave the form as they appear. A disabled field leaves the form: it is not checked, does not block the submission and does not get focus. Server rendering registers nothing.
Fields of the library register by themselves. A control of your own takes part through the useValidation composable, see Custom fields.
Server errors
An error known only after the data is sent is passed to the field in its error prop: the field shows it, and the form is invalid while it is set. See Server errors.
API
Props
| Name | Type | Default | Description |
|---|---|---|---|
validate-on | "blur" | "input" | "submit" | undefined | When the fields check their rules: blur, input or submit. A field's own validate-on
wins. blur by default. |
no-error-focus | boolean | false | Keeps focus where it is when a submission fails, instead of moving it to the first error. |
model-value | null | false | true | null | Form validity: true when every field with rules is checked and valid, false when a field
shows an error, null while some field is unchecked. Read-only for the app: a written value
is replaced with the actual state. |
Events
| Name | Signature | Description |
|---|---|---|
update:modelValue | (value: boolean | null) => void | Emitted when modelValue changes. Used for two-way binding (v-model). |
invalid | (errors: SFormError[]) => void | A submission failed validation. |
submit | (event: SubmitEvent) => void | The form passed validation. Without a listener the form is submitted natively. |
reset | (event: Event) => void | The form was reset; the errors are cleared on the next tick, after the app resets its data. |
Slots
| Name | Description |
|---|---|
default | Form content. Receives { valid, validating, errors, validate, resetValidation }. |