Comments (29)
Found a nice way for myself. Maybe it will help somebody. Works with parent-chidren references.
The code below is from a save() component method. Vee-Validate using globally.
let promises = []
for (let child in this.$refs) promises.push(this.$refs[child].$validator.validateAll());
Promise.all(promises)
.then(this.$validator.validateAll())
.then(
() => {//OK CONTINUE SAVING},
() => {//NOT OK})
@logaretm I am afraid that I absolutely not professional but I would add this snippet in a method sounds something like validateAllWithRefs() in my lib. If you OKay with it I think I can do my first Pull-request))
from vee-validate.
Well in Vue 2.0 the component can only communicate via the old API by $emit, both $broadcast and $dispatch were deprecated, so I don't think It will be included in the plugin since there is no distinction between a form component with multiple children acting as custom inputs and say a layout with some inputs in it.
But I think you can do it in a couple of ways, depending on Vue version:
Vue 1.0
In Vue 1.0 you can trigger the event on all child components, you just need to hook into the event and broadcast it again to the children.
ready() {
this.$on('veeValidate', () => {
this.$broadcast('veeValidate');
});
}
// Triggering validateAll should now trigger validations for all child components.
this.$validator.validateAll();
Vue 2.0
You can use an empty Vue instance as an event bus:
// in bus.js
import Vue from 'vue';
const bus = new Vue();
export default bus;
// In your parent component
import bus from './bus';
mounted() {
this.$on('veeValidate', () => {
bus.$emit('veeValidate');
});
}
// in your child components.
import bus from './bus';
mounted() {
bus.$on('veeValidate', () => {
this.$validator.validateAll());
});
}
or you can go ahead and loop over the child components and trigger validateAll individually on each of them:
// in your parent component
// you might want to recursively loop over the instances.
this.$children.forEach(vm => {
vm.$validator.validateAll();
});
I didn't test those suggestions, but I can build an example if you continue to have trouble solving this.
I would love to offer some better way built into the plugin, but until I figure a nice way to do it, I don't think it will be in time for full release tho.
from vee-validate.
Thanks @prsth, I think it's one of the cleanest solution I found here to make validation working with a dynamic and very nested component. 👍
This is what I am using right now:
const promises = [];
promises.push(this.$validator.validateAll());
// ref `widget-form` can be not rendered, so we resolve `false` by default
promises.push(this.$refs['widget-form'] ? this.$refs['widget-form'].$validator.validateAll() : Promise.resolve(false));
Promise.all(promises).then(validations => {
// If one validation has failed, we stop here
if (validations.some(validation => validation === false)) return;
// Everything is valid
});
from vee-validate.
For me it works fine by adding inject: ['$validator']
in all nested components
export default {
name: 'child-component',
inject: ['$validator'],
}
from vee-validate.
Since this thread is not yet locked for further comments, I'd like to add a solution I built for v4 in case anyone else like me is scouting around trying to find out how to do this properly with Vue3 and Composition API.
It works by adding each
validate
method fromuseForm
to an array and then on form submit it will loop through the array to ensure all validators pass asynchronously. The idea is borrowed from a reply from @logaretm in this issue: #2915formAggregator.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>
@joakimriedel Thanks a lot for your example! I was looking for something like this!
@logaretm I think adding something like this useValidationAggregator
to the library would be very useful, I think that it is quite common to have components that encapsulate a slice of the form that is dynamic or just repeats (asking for several children/employee info, several addresses, etc...) and to have a parent component that needs to check that all the children are valid...
from vee-validate.
@shakee93 I have just added an example Gist demonstrating the principle of how we passed the error bag bag to the parent element.
https://gist.github.com/sproogen/147d75db261505e8a558a7fd11a20551
@logaretm might have another suggestion on how this could be done though?
from vee-validate.
Since this thread is not yet locked for further comments, I'd like to add a solution I built for v4 in case anyone else like me is scouting around trying to find out how to do this properly with Vue3 and Composition API.
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. The idea is borrowed from a reply from @logaretm in this issue: #2915
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>
from vee-validate.
I had already done it, so I thought I would pull that out and stick it in the gist.
Feel free to modify it and use is as an example in the future.
from vee-validate.
Yes, App.event is just a new Vue()
instance used as the event bus. In my example, I have a global App that it attaches too but it would be different depending on your setup and where you place the event bus.
from vee-validate.
@ahmed-essawy Why nobody is reacting/commenting on this solution it is really the cleanest and most obvious so far. Is there any drawbacks in using it, or i am missing something?
from vee-validate.
Thanks for the response,
We are using Vue2 so I thought that an event bus would be the best approach to this. But I was unsure if it was something that might be included in the plugin.
I think we should be good to implement this our selves but it might be good to include something about this in the docs, maybe as an advanced example.
Thanks
from vee-validate.
Yea sure, I might add an example regarding this, thanks!
from vee-validate.
Thanks for the great plugin. little doubt. how am i supposed to check if there are any errors in child component this.errors.any()
returns false currently. any workaround for this ?
from vee-validate.
@sproogen that is pretty much how I would have handled it, yours are even cleaner. Sorry I couldn't create an example for this, thanks for the gist!
from vee-validate.
Another option could be to have child components register themselves with the parent to be validated at the same time:
In the code below, App.event is a Vue object used as the event bus.
Parent Component:
...
mounted: function(){
var self = this;
this.childValidators = [];
App.event.$on('child-validator-added', function(component){
self.childValidators.push(component);
});
App.event.$on('validate', function(){
self.$validator.validateAll();
self.childValidators.forEach(function(component){
component.$validator.validateAll();
component.$validator.getErrors().errors.forEach(function(error){
self.errors.add(error.field, error.msg);
})
})
});
$('#resource-form').submit(function(e){
e.preventDefault();
App.event.$emit('validate');
if (self.errors.count()) {
console.log('ERRORS')
} else {
console.log('SUCCESS')
}
});
},
Child Component:
...
mounted: function(){
var self = this;
this.$nextTick(function(){
App.event.$emit('child-validator-added', this);
});
}
from vee-validate.
@sproogen I have used your example, but I keep getting the error "TypeError: _vm.errors.has is not a function". It is properly not your example, but I cannot solve the error.
from vee-validate.
The example above doesn't directly call the method "has()" on errors. Is that somewhere in your code by chance? If so, what does it look like?
from vee-validate.
@daylightstudio I did not use your example, but the example of @sproogen.
https://gist.github.com/sproogen/147d75db261505e8a558a7fd11a20551
But I have a question for you. From where do you get App.Event? Is this a new Vue Instance?
//Rasmus
from vee-validate.
This is not really usefull... validateAll() is now a promise. So none of these samples really solve the issue.
from vee-validate.
@pimboden There are already multiple solutions to issues like this depending on your usage or needs, you just have to be consistent in your code.
for example if your custom components serve as a custom input with special behavior then you should use component validation which is supported, no need to transfer errors via events or anything.
if your components serve as organizing or grouping of some inputs, then you should use the events.
I'm considering adding a centralized error object which is optional for such cases, but it will require complex scoping, so I hope I will be able to have a general idea about it after releasing the new version.
from vee-validate.
@logaretm Thank you. But when you say "there are already multiple solutions to issues like this" , could you tell me where... I have goggled, and I only found 2 or 3 solutions, all working with validateAll() without using it as promise...
My app, uses a component (form-component) that has some input fields,
<template>
<div>
<input type = text -..... ></text>
<vss-inputs...> </vss-inputs>
</div>
<template>`
Inside it uses a child-component, that only loops through some array, and depending on the values of the array, this child-component renders its own child-components: So my vss-inputs is something like this
<template v-for="(vss, index) in vssToRender">
<template v-if="showCheckbox(vss)||showCheckboxRequired(vss)">
<vss-subcomponent-1...> </vss-subcomponent-1>
</template>
<template v-if="showCheckbox(vss)||showCheckboxRequired(vss)">
<vss-subcomponent-2> </vss-subcomponent-2>
</template>
</template>
I didn't' find out, how my "form-component" gets informed that all components in the hierarchy have finished validating.
My form. component triggers an event for validating.
All my sub-components, go through their validations.. (they correctly render the error messages)
The problem... how does my "form-component" get informed that all other components finished validating? And how does it know if some sub components didn't pass the validation?
Thanks for your help
from vee-validate.
I understand that those "solutions" aren't documented, but its hard to document every single solution that may not fit all projects, ideally the plugin should be easy enough for users to implement their own solutions for such problems.
having said that, your issue seems a little bit more complex than the others mentioned, but you can create a dedicated event bus for the errors, whenever a component gets created, it should register itself as an "error provider" meaning the parent knows of such a component.
You can then trigger validation across all registered components, by looping over their container (array) and triggering validateAll
then you add the errors in the catch
callback to the parent errors, using Promise.all
and collecting all promises can tell you when all validations are finished validating.
I have not implemented such a thing, but this comes to my mind as a possible solution for your case.
from vee-validate.
@prsth I have just tested this method at my side and it seems to work pretty well, I just need to add the ref
property to my custom component. The ref
value should be same as name
I think?
from vee-validate.
@coderabsolute I guess you can name it in a way you would like to. It doesn't matter. It even could be an evaluated string like this
<template v-for="(item, n) in myarray">
<child-component ref="`anyname${n}`"></child-component>
</template>
All the child components would be validated.
from vee-validate.
@ahmed-essawy Why nobody is reacting/commenting on this solution it is really the cleanest and most obvious so far. Is there any drawbacks in using it, or i am missing something?
Thanks @Thrajnor,
It's working perfectly since I wrote the comment without issues till now
from vee-validate.
@ahmed-essawy Why nobody is reacting/commenting on this solution it is really the cleanest and most obvious so far. Is there any drawbacks in using it, or i am missing something?
Thanks @Thrajnor,
It's working perfectly since I wrote the comment without issues till now
@ahmed-essawy Hello, I do this, but no working for me, this is my code:
Code parent:
...
<v-form
novalidate
@submit.prevent="validateBeforeSubmit">
....
<v-flex md3>
<v-text-field
v-validate="'required'"
v-model="form.sigla"
:error-messages="errors.collect('Sigla')"
label="Sigla"
type="text"
data-vv-name="Sigla"
required
/>
</v-flex>
<!-- child component -->
<core-regra-financeira
v-model="regra"
/>
<v-btn
:loading="loadingBtn"
type="submit"
color="primary"
>Submit</v-btn>
...
</form>
...
methods: {
validateBeforeSubmit () {
this.$validator.validate()
.then(result => {
if (result) {
// OK
} else {
// NOK
}
})
Code Child:
...
<v-flex
md2
sm6
xs12>
<v-text-field
v-formata-moeda="value.limiteCreditoMesMaximo"
v-money="money"
v-validate="'required'"
v-model.lazy="value.limiteCreditoMesMaximo"
:disabled="disabled"
:error-messages="errors.collect('Limite Crédito Mês Máximo')"
data-vv-name="Limite Crédito Mês Máximo"
required
type="tel"
suffix="R$"
reverse
label="Limite Crédito Mês Máximo"
/>
</v-flex>
export default {
inject: ['$validator'],
...
This only works if the user changes the value of the form, otherwise the validation does not work.
Do you have an example?
from vee-validate.
Hello @robertoconceicao
try to use this.$validator.validateAll().then();
instead of this.$validator.validate().then();
if it still not working
try to use refs as below
<v-form @submit.prevent="validateBeforeSubmit" ref="exampleForm">.....</v-form>
then change validation method to be this.$refs.exampleForm.$validator.validateAll().then();
instead of this.$validator.validate().then();
from vee-validate.
Thanks @ahmed-essawy
I try the two solution, but not working still.
I resolved passing into child the method save of parent, it's working for me, in this case.
My solution:
Code Parent:
<form>
fields parent
<!-- child component -->
<core-regra-financeira
v-model="regra"
:disabled="disabled"
:salve="validateBeforeSubmit" <!-- Here, I'm passing into method validate and save of Parent for child -->
:go-back="goBack"
:cancel="cancel"
/>
</form>
Code Child:
fields childs required
....
export default {
inject: ['$validator'],
props: {
value: {
type: Object,
required: true
},
save: {
type: Function,
required: true
},
go-back: {
type: Function,
required: true
},
cancel: {
type: Function,
required: true
},
// eslint-disable-next-line vue/require-default-prop
disabled: false
},
methods: {
validationChild () {
this.$validator.validate()
.then(result => {
if (result) {
this.save() // callback from Parent
} else {
// NOK alert
}
})
}
}
from vee-validate.
Has a little better by-default?
from vee-validate.
Related Issues (20)
- Not assignable to parameter of type 'ZodType<any, ZodTypeDef, any>' error on toTypedSchema HOT 1
- Dynamic custom checkbox and dynamic input value update issue - Composition API
- Trying to set a custom error message using setFieldError against a Field HOT 1
- Custom input not working with yup.ref() HOT 1
- Allow resetField() to set (or keep) the "initialValue" associated to a field HOT 6
- `@vee-validate/valibot` not yet compatible with Valibot 0.31.0 HOT 1
- Checkbox not working in dynamic loop
- Mix of Nested objects and Avoiding nesting HOT 1
- Unable to set a default value on a checkbox field HOT 3
- Registering @vee-validate/rules in typescript produces typing errors HOT 1
- Type Error `errorBag` using array in form
- Use conditional Validation Schema HOT 2
- touched from useField not triggered after touch field HOT 1
- Custom Localization Files
- validateField can't access nested properties
- Unable to setFieldError from API with multi-step form wizard in before step HOT 1
- Pass submit function to useForm composable
- best way to set useField value from props ?
- useForm not working after build when using shadcn components from a shared package in monorepo HOT 2
- defineField ref's value is possibly undefined, even if the schema doesn't allow it HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from vee-validate.