Skip to content

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.

Up to 200 characters

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:

  • null until 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;
  • true when every field with rules has been checked and passed;
  • false as soon as any field shows an error, a rule error or a manual error.

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 propMeaning
validthe same value as v-model: true, false or null
validatingwhether an async rule is running in some field
errorsthe fields that show an error now, in page order (SFormError[])
validatechecks every field, see Methods
resetValidationclears 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:

v-model: null

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 }, where errors lists the failed fields in page order. It shows the errors like a submission, but does not move focus and does not emit submit or invalid;
  • 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 invalid with 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-focus keeps focus where it is;
  • the form is valid and has a submit listener: it emits submit with the original SubmitEvent. Sending the data is up to the app;
  • the form is valid and has no submit listener: 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

NameTypeDefaultDescription
validate-on"blur" | "input" | "submit"undefinedWhen the fields check their rules: blur, input or submit. A field's own validate-on wins. blur by default.
no-error-focusbooleanfalseKeeps focus where it is when a submission fails, instead of moving it to the first error.
model-valuenull | false | truenullForm 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

NameSignatureDescription
update:modelValue(value: boolean | null) => voidEmitted when modelValue changes. Used for two-way binding (v-model).
invalid(errors: SFormError[]) => voidA submission failed validation.
submit(event: SubmitEvent) => voidThe form passed validation. Without a listener the form is submitted natively.
reset(event: Event) => voidThe form was reset; the errors are cleared on the next tick, after the app resets its data.

Slots

NameDescription
defaultForm content. Receives { valid, validating, errors, validate, resetValidation }.