How to use vee-validate in a parent-child relationship
Asked Answered
I

5

7

I have a couple of components that need to be validated. The problem is that that inputs are in a child component while the submit button is in the parent like this

ParentComponent.js

<ValidationObserver v-slot="{ passes }"> 
     <form @submit.prevent="passes(someSubmitFunction)">
         <ChildComponent />
     </form>
</ValidationObserver>

ChildComponent.js

     <ValidationProvider rules="required" v-slot="{ errors }" name="vendorName">
         <el-input />
      </ValidationProvider>

What am I doing wrong here?

Irrelievable answered 6/11, 2019 at 16:11 Comment(0)
P
2

Luckily there is a pattern for sharing validator scope between parent and child components! You can inject the validator from the parent into the child so that they use the same instance. This means that you can validate child inputs from the parent using this.$validator.validate()

Docs: http://vee-validate.logaretm.com/v2/concepts/injections.html

Pewit answered 6/11, 2019 at 19:51 Comment(3)
what's the solution for v3? In v3 , there's no more $validator – Stelmach
@Stelmach you have to use a ValidationProvider and ValidationObserver. That's about all I know, I chose to stay on v2 since it was much simpler. – Pewit
@Stelmach saw this one year later while looking for v4 solution, added a suggested answer below ... so if you're still scratching your head look no further πŸ˜„ – Fatherless
F
5

you can overwrite instance of chid component validator by parent component validator in created hook like this.

created() {
 this.$validator = this.$parent.$validator
}

notice: if you use mounted it dosent work because before DOM rendering replacing must be done.

Fail answered 11/10, 2021 at 8:54 Comment(0)
F
5

For people like me, coming here for guidance in the future with v4 of vee-validate together with Vue3 Composition API and Typescript, I'd like to suggest this answer. It works by adding each validate method from useForm to an array and then on form submit it will loop through the array to ensure all validators pass asynchronously.

formAggregator.ts

import { InjectionKey, provide, ref } from "vue";

type ValidatorMethodType = () => Promise<{ valid: boolean }>;

export const AddValidatorKey: InjectionKey<
  (validator: ValidatorMethodType) => void
> = Symbol("AddValidatorKey");

export function useValidationAggregator() {
  const validators = ref<ValidatorMethodType[]>([]);

  // used by components to add validators to the array
  const register = (validator: ValidatorMethodType) => {
    validators.value.push(validator);
  };

  // provide this aggregator to components
  provide(AddValidatorKey, register);

  // run all validators
  const validateAll = async () => {
    return Promise.all(validators.value.map((v) => v()));
  };

  return {
    validateAll,
    register,
  };
}

ParentComponent.vue

<template>
<form @submit.prevent="onSubmit">
    <ChildComponent />
   <button type="submit" :disabled="inSubmit">Submit</button>
</form>
</template>
<script setup>
import useValidationAggregator from "./formAggregator.ts"
const { validateAll } = useValidationAggregator();
// note: you can use register() to also add validator from the parent component

const inSubmit = ref(false);
const onSubmit = async () => {
   try {
      inSubmit.value = true;

      // run all form validators
      const results = await validateAll();

      // ensure all are valid
      if (results.some((r) => !r.valid)) {
         return;
      }

      // todo: post form or whatever you need after validation passes
   } finally {
      inSubmit.value = false;
   }
}
</script>

ChildComponent.vue

<template>
   <!-- some validated fields -->
</template>
<script setup>
import { AddValidatorKey } from "./formAggregator.ts"

const { validate } = useForm({ ... setup validated fields ... });

// add this component's validator to the aggregator
const addValidator = inject(AddValidatorKey, () => {
  throw new Error("No aggregator provided for inject");
});
addValidator(validate);
</script>
Fatherless answered 8/11, 2021 at 18:1 Comment(2)
firstly Thank You for this solution... small doubt, what if i have multiple models eg: Project, Task. How would validateAll() only validate the model to which submit button has been clicked? – Capacitance
@Capacitance this solution is only for when a single form has one or many child components – Fatherless
P
2

Luckily there is a pattern for sharing validator scope between parent and child components! You can inject the validator from the parent into the child so that they use the same instance. This means that you can validate child inputs from the parent using this.$validator.validate()

Docs: http://vee-validate.logaretm.com/v2/concepts/injections.html

Pewit answered 6/11, 2019 at 19:51 Comment(3)
what's the solution for v3? In v3 , there's no more $validator – Stelmach
@Stelmach you have to use a ValidationProvider and ValidationObserver. That's about all I know, I chose to stay on v2 since it was much simpler. – Pewit
@Stelmach saw this one year later while looking for v4 solution, added a suggested answer below ... so if you're still scratching your head look no further πŸ˜„ – Fatherless
N
0

When running validation for parent ValidationObserver it validate child ValidationObserver too.
If you need validate only parent ValidationObserver you can use "disabled" props for child observer.
https://vee-validate.logaretm.com/v3/api/validation-observer.html#props

<ValidationObserver ref="parent">
  <ValidationObserver ref="child" :disabled="true">
    <ValidationProvider rules="required">
      <!-- This child observer will not be validated with parent validation -->
    </ValidationProvider>
  </ValidationObserver>
</ValidationObserver>
Noctilucent answered 26/5, 2022 at 0:14 Comment(0)
S
-1

you can use a ref on the child component then try to do a validation on that ref when calling someSubmitFunction

Something like this :

  const isValid = await this.$refs.childref.validate()
Sphere answered 6/11, 2019 at 16:25 Comment(1)
Actually I don't think you can. When I call my child observer ref like this; const isValid = await this.$refs.consentPanel.validate(); it throws an exception about validate is not a function. – Prostatectomy

© 2022 - 2024 β€” McMap. All rights reserved.