Categories
Vue

Vee-Validate — Persisting Validation State and Nesting ValidationObservers

Spread the love

Form validation is a feature that’s not built into Vue.js.

However, we still need this feature very much.

In this article, we’ll look at how to persist form validation states for unmounted components.

Also, we look at how to nest ValidationObservers .

Persisting Provider State

We can persist validation state with the keep-alive component.

With it, even if the component isn’t displayed, the components inside it won’t be unmounted.

For example, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import { ValidationProvider, ValidationObserver, extend } from "vee-validate";
import { required, alpha, email } from "vee-validate/dist/rules";

extend("required", required);
extend("alpha", alpha);
extend("email", email);

Vue.component("ValidationProvider", ValidationProvider);
Vue.component("ValidationObserver", ValidationObserver);

Vue.config.productionTip = false;

new Vue({
  render: h => h(App)
}).$mount("#app");

App.vue :

<template>
  <div id="app">
    <ValidationObserver ref="form">
      <form @submit.prevent="onSubmit">
        <keep-alive>
          <ValidationProvider
            v-if="currentStep === 1"
            name="email"
            rules="required|email"
            v-slot="{ errors }"
          >
            <input v-model="email" type="text" placeholder="email">
            <span>{{ errors[0] }}</span>
          </ValidationProvider>
        </keep-alive>

<keep-alive>
          <ValidationProvider v-if="currentStep === 2" rules="required|alpha" v-slot="{ errors }">
            <input v-model="name" name="name" type="text" placeholder="name">
            <span>{{ errors[0] }}</span>
          </ValidationProvider>
        </keep-alive>

<button type="button" @click="goToStep(currentStep - 1)">Previous</button>
        <button
          type="button"
          [@click](http://twitter.com/click "Twitter profile for @click")="goToStep(currentStep + 1)"
        >{{ currentStep === 2 ? 'Submit' : 'Next' }}</button>
      </form>
    </ValidationObserver>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      currentStep: 1,
      name: "",
      email: ""
    };
  },
  methods: {
    goToStep(step) {
      if (step < 1) {
        return;
      }

      if (step > 2) {
        this.onSubmit();
        return;
      }
      this.currentStep = step;
    },
    async onSubmit() {
      const success = await this.$refs.form.validate();
      if (!success) {
        return;
      }
      alert("submitted");
      this.name = "";
      this.$nextTick(() => {
        this.$refs.form.reset();
      });
    }
  }
};
</script>

We have a multi-step form.

Each field is wrapped inside a keep-alive component.

ValidationProvider is inside keep-alive .

This way, we can keep the validation state of the field even if it’s hidden by v-if .

We have 2 buttons to go to the next step and go back to the previous step with the goToStep method.

The onSubmit method checks the form with the ValidationObserver ‘s ref, which returns a promise that resolves to true is all fields are valid and false otherwise.

We proceed with form submission if success is true .

Nested Observers

We can nest ValidationObservers .

All the fields are checked by the parent ValidationObserver if they’re nested.

For example, we can write:

<template>
  <div id="app">
    <ValidationObserver ref="form">
      <form @submit.prevent="onSubmit">
        <ValidationObserver>
          <ValidationProvider name="email" rules="required|email" v-slot="{ errors }">
            <input v-model="email" type="text" placeholder="email">
            <span>{{ errors[0] }}</span>
          </ValidationProvider>
        </ValidationObserver>

<ValidationObserver>
          <ValidationProvider rules="required|alpha" v-slot="{ errors }">
            <input v-model="name" name="name" type="text" placeholder="name">
            <span>{{ errors[0] }}</span>
          </ValidationProvider>
        </ValidationObserver>

        <br>

        <input type="submit" value="submit">
      </form>
    </ValidationObserver>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      name: "",
      email: ""
    };
  },
  methods: {
    async onSubmit() {
      const success = await this.$refs.form.validate();
      if (!success) {
        return;
      }
      alert("submitted");
      this.name = "";
      this.email = "";
      this.$nextTick(() => {
        this.$refs.form.reset();
      });
    }
  }
};
</script>

We have ValidationObserver component inside another one.

Both fields are still validated as a whole by the root ValidationObserver .

We can also write:

<template>
  <div id="app">
    <ValidationObserver ref="form" v-slot="{ handleSubmit }">
      <form @submit.prevent="handleSubmit(onSubmit)">
        <ValidationObserver>
          <ValidationProvider name="email" rules="required|email" v-slot="{ errors }">
            <input v-model="email" type="text" placeholder="email">
            <span>{{ errors[0] }}</span>
          </ValidationProvider>
        </ValidationObserver>

        <ValidationObserver>
          <ValidationProvider rules="required|alpha" v-slot="{ errors }">
            <input v-model="name" name="name" type="text" placeholder="name">
            <span>{{ errors[0] }}</span>
          </ValidationProvider>
        </ValidationObserver>

        <br>

        <input type="submit" value="submit">
      </form>
    </ValidationObserver>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      name: "",
      email: ""
    };
  },
  methods: {
    onSubmit() {
      alert("submitted");
      this.name = "";
      this.email = "";
      this.$nextTick(() => {
        this.$refs.form.reset();
      });
    }
  }
};
</script>

We use the built-in handleSubmit function to check for form validation state before we run our own onSubmit method.

Therefore, we don’t need to call validate on the root ValidationObserver ‘s ref any more.

Conclusion

We can persist validation state if a component is unmounted with the keep-alive component.

Also, we can nested ValidationObserver components.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *