vee-validate
vee-validate copied to clipboard
useForm - meta.valid false even tho there are no errors?
What happened?
I got multiple Forms that get shown one after the other. If I am at step 5 or whatever and I get back to a previous step like step 2 the form of step 2 gets shown but it's no longer valid even tho it was before.
Even if I then change the values of the form again the meta.valid state doesn't get updated while the errors get set correctly.
interface SchadenursacheData {
schadenursache: SchadenursacheTypes;
valid: boolean;
}
const { handleSubmit, meta, values, errors } = useForm<SchadenursacheData>({
validationSchema: object({
schadenursache: string().required("Bitte angeben"),
}),
});
This is the "failed state" after returning to the form:
Reproduction steps
Sorry I don't have reliable repro steps outside our codebase.
Version
Vue.js 3.x and vee-validate 4.x
What browsers are you seeing the problem on?
- [X] Firefox
- [X] Chrome
- [X] Safari
- [ ] Microsoft Edge
Relevant log output
No response
Demo link
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
The valid
flag is independent from the errors
existence, because a silent validation is run initially.
Do you mind creating an example for this?
Could take a bit to repro. Will report back once I have a good reproduction.
But besides that is there any way to debug why the valid flag is false? Instinctively you'd think that if there are no errors, the form is valid.
You are right that's a perfectly valid way to think about it but from usability perspective, let's say you want to disable the submit until the form is valid. Now if the valid
flag is just a reflection of errors existence, you can never do that because we didn't validate the form yet so we have no idea if it is valid or not and thus it is assumed to be valid when it can be the other way around.
Form validity has actually 3 values, valid
, invalid
and unknown
. We have no way to represent the last value with booleans which is why it can never satisfy all needs. It's a tricky subject which is hard to get right.
I see. It doesn't really reflect my situation though I guess. Once I change any value in the form the validation is triggered. Let's say I input regular text in a field with a numbers-only restriction. An error is placed inside the error object of useForm
and shown to the user. So validation must have taken place. Isn't it safe to assume that the valid
value in this case should be invalid
and if I then correct the input to be a number the value to be valid
again?
Just trying to understand the meta object correctly so I got an easier time in the future. :) Thank you so far.
(Btw. still working on that repro, don't have much time atm but will get back to you.)
got the same error and this entirely breaks my apps, since i also use meta.valid
to check if the form has no error then the user can submit the form. i think it is instinctively meta.valid
is refer to if the form is valid and has no error.
the problem i ecounter is, using a modal with input, when initially the modal render, it has no problem, but the thing happen when user close the modal and then re open it, the validation did not work and user suddenly cannot submit the form. in my cases the user does not need to input anything, just submitting the modal
I have the same issue. Basically, meta.valid
is useless, so I'm using !!Object.keys(errors).length
to check whether the form is valid, which is super ugly and non-intuitive.
I understand that it might not be ideal to use meta.valid
as an indicator for the presence of errors, but then there should be a prop that does exactly that because that's how most people use it and it's the intuitive way to think about form validation.
This is how I'd like to think: Does my form have errors, if so block submission, otherwise, it's safe to continue. I should assume that at every update:model-value
and initially when the form is rendered the validation is run, and if at any point there's an error in my form, it should be inside the errors
object. If I ever second-guess myself, it should be due to a pending (meta.pending
) validation, and nothing else. Not being able to assume these intuitive rules is... non-intuitive at best.
Also, I notice that a lot of times meta.pending
is true, which is weird, like, shouldn't it only be true during the async operation of validation?
Got the same issue. The problem in my case was the incorrect value validation:
I had an required field wich were a select input. After selecting a value errors
were empty and valid
was false. So I did something crazy, I had computed variable inside disabled
property on my submit button. In the computed callback I ran validate
+ logs. This caused the computed to be triggered every tick, AND I got the errors
with message that my required field is not set. But the field is in values
and also have correct value.
UPD: At the end I found out, that the error appeared after some code with 'useField' were moved to separat component. I think if useField
is too deeply nested - this error can appear :)
UPD2: The issue were in v-if
which waited for the data. So if useField
is used later, the error appears.
I hit this error with the following minimal reproducible example:
const validationSchema = toTypedSchema(
z.object({
day: z.string().optional(),
month: z.string().optional(),
year: z.string().optional(),
}).refine((input) => {
if ([input.day, input.month, input.year].some(Boolean))
return [input.day, input.month, input.year].every(Boolean)
return true
}, {
message: 'Please fill out all fields for patient birth date',
path: ['day', 'month', 'year'],
}),
)
const { values, errors } = useForm({
validationSchema
})
const isFormValid = useIsFormValid()
I use zod.refine to make the date fields either all optional or all required. After more debugging, the errors object only populates if the day
field is dirtied. month
and year
never update with the error. I'm able to chain multiple refine()
s, each with the path
array being a different field and it works as expected, but having each path in one array is breaking the non-first places. Maybe I'm misunderstanding the purpose of path
in the zod refine
call, but I still get isFormValid
is false with errors
being empty.
I ran into this issue several times and can't trace what is happening, nice investigation @senyaak [comment] and @Jtcruthers [comment]! That helped me feel more confident I'm not doing something wrong, I just have a wrong understanding of vee-validate.
Details of my observation
What I noticed just now was upon page load `meta.valid` was false with no errors. The form was correctly invalid: a required field was empty. It was only when I put my cursor into the input, typed a value, cleared the value, and blurred the field that I was able to see an error.Ultimately I'm OK with that behavior -- if the form is invalid it should be invalid. However, the lack of observability on WHY the form was invalid caused me significant time loss. I have a complex form (with complex yup
s) so I made a bad assumption that my yup
was wrong, when really the yup
was correct, but the data was truly invalid; I just couldn't see WHICH data was invalid.
My expectation is if meta.valid
is false there would be some way to observe why its invalid. What way can that be observed? I assumed errors
would have it but I see comments explaining why that might not be the right place. Perhaps there is some hook/callback where the observation can happen?
We have a vue-devtools plugin that gets auto-installed it does help with cases of hidden errors or why a form isn't submitting or why it is valid.
But it doesn't help with that first initial case, I'm starting to lean more and more towards making meta.valid
be just a reflection of errors existence and moving the error display logic to user-land.
This would eliminate a lot of issues i'm getting with "why validation ran" and allows validation triggers to truly reflect what they do.
Example of that thought:
<script>
const { value, errorMessage, meta } = useField('field');
</script>
<template>
<input v-model="value">
<span v-if="meta.touched || meta.dirty">{{ errorMessage }}</span>
</template>
It is more verbose but removes that "confusion" from how it works under the hood.
For the zod refines, I think they are confusing for the most part because they run if all the object properties exist or had a similar limitation. I don't remember correctly but I would not include them with this issue as the same one.
If I go with this it would be at v5 since this is a major change.