Validation Observer

Using providers for validation is very handy but it introduces few usage problems of its own, for example how would you tell the current state. Let's say you want to disable a button as long as the form is invalid, how would you do that?

The ValidationObserver is a convenient component that also uses the scoped slots feature to communicate the current state of your inputs as a whole.

Here is a small example, again with Vuetify components wrapped by the Provider's withValidation method:

<ValidationObserver v-slot="{ invalid }">
  <form @submit.prevent="submit">
    <InputWithValidation rules="required" v-model="first" :error-messages="errors" />

    <InputWithValidation rules="required" v-model="second" :error-messages="errors" />

    <v-btn :disabled="invalid">Submit</v-btn>
  </form>
</ValidationObserver>

Rendering

Like providers, observers render a span by default. You can customize the rendered tag using the tag prop, for example a form tag might be more useful.

<!-- Render a form -->
<ValidationObserver tag="form">
  <!-- Fields -->
</ValidationObserver>

You can expand upon this by adding your form listeners like submit on the observer directly:

<!-- Render a form -->
<ValidationObserver tag="form" @submit.prevent="onSubmit">
  <!-- Fields -->
</ValidationObserver>

Scoped Slot Data

The scoped slot is passed an object containing a flags object representing the merged state of all providers registered under the observer. It contains the following properties:

Name Type Description
dirty boolean True if at least one field is dirty.
pristine boolean True if all fields are pristine (not dirty).
valid boolean True if all fields are valid.
invalid boolean True if at least one field is invalid.
pending boolean True if at least one field's validation is in progress.
touched boolean True if at least one field has been touched (blurred).
untouched boolean True if all fields haven't been touched (blurred).
errors { [x: string]: string[] } An object containing reference to each field errors, each field is keyed by its vid prop.
validate () => { then: () => Promise<any> } A method that triggers validation for all providers. Can be chained using then to run a method after successful validation.
reset () => void A method that resets validation state for all providers.

Examples

Validate Before Submit

Validating before submit is even easier than the old way, using the public methods and a simple ref we can validate all providers before submitting the form.

<template>
  <ValidationObserver ref="observer" v-slot="{ invalid }" tag="form" @submit.prevent="submit()">
    <InputWithValidation rules="required" v-model="first" :error-messages="errors" />

    <InputWithValidation rules="required" v-model="second" :error-messages="errors" />

    <v-btn :disabled="invalid">Submit</v-btn>
  </ValidationObserver>
</template>

<script>
export default {
  methods: {
    async submit () {
      const isValid = await this.$refs.observer.validate();
      if (!isValid) {
        // ABORT!!
      }

      // 🐿 ship it
    }
  }
};
</script>

If you plan to trigger validation from the template without using refs you can use the validate method present in the scopedSlot data.

<template>
  <ValidationObserver v-slot="{ invalid, validate }">
    <form @submit.prevent="validate().then(submit)">
      <InputWithValidation rules="required" v-model="first" :error-messages="errors" />

      <InputWithValidation rules="required" v-model="second" :error-messages="errors" />

      <v-btn :disabled="invalid">Submit</v-btn>
    </form>
  </ValidationObserver>
</template>

As you have guessed, the validate method on the Observer's scopedSlot is thenable, meaning you can chain another method to run after the validation passes like the form submission handler. Note that the validate method does not return a promise, but a promise-like object that has a then method for convenience, which can be also chained further.

TIP

Using the same approach you can reset validation state for all providers using the public method reset().

You can see observers in action here

Scopes And Groups

The Validation Components API does not implement scopes and won't be, you can use the ValidationObserver to group your fields without the complexties of the scopes API by using multiple observers and refs.

<template>
  <div>
    <ValidationObserver tag="form" ref="obs1" v-slot="{ invalid }">
      <!-- Fields -->
    </ValidationObserver>

    <ValidationObserver tag="form" ref="obs2" v-slot="{ invalid }">
      <!-- Fields -->
    </ValidationObserver>
  </div>
</template>

<script>
// Somewhere in a method ...
// validate the first observer.
this.$refs.obs1.validate();

// validate the second observer.
this.$refs.obs2.validate();
</script>

Simple and clean.

Nested Observers

Building upon the previous example, observers can be nested to create nested forms for advanced use-cases. The outmost observer is able to trigger validation/resets on child obeservers and providers. Its state is also synced with the child observers and providers alike.

<ValidationObserver ref="op" v-slot="observer">
  <ValidationObserver ref="oc">
    <ValidationProvider rules="required" v-slot="provider">
      <input type="text" v-model="value">
      <span>{{ provider.errors[0] }}</span>
    </ValidationProvider>
    <!-- This is synced with the state of all children providers/observers -->
    <pre>
      {{ observer }}
    </pre>
  </ValidationObserver>
</ValidationObserver>

Reference

Below is the reference of the ValidationObserver public API.

Props

Prop Type Default Value Description
tag string span The default tag to render.

Methods

Those are the only methods meant for public usage, other methods that may exist on the ValidationObserver are strictly internal.

Method Args Return Value Description
validate void Promise<boolean> Validates all the child providers/observers and also mutates their state.
reset void void Resets validation state for all child providers/observers.

Events

The validation observer does not emit any events at this time.