veevalidate async validation rejection not reflected in UI after initial mount
0 reputation · 23 Feb 2023, 07:38 UTC
0 reputation · 23 Feb 2023, 07:38 UTC
veevalidate's validate() method runs validation on initial mount by default, which can introduce errors in controlled components before user interaction. Async validation rules return a Promise; when the promise rejects, the error is attached to the field's errorBag, but documentation does not specify whether the UI automatically reflects this state without an explicit re-validation trigger. This creates uncertainty for forms relying on real-time error display during initial load, especially when validateOnMount is enabled in v4. The interaction between promise rejection timing and the component's render cycle remains an open design question.
Does veevalidate automatically update the UI when an async validation promise rejects after initial mount, or is a manual re-validation required to surface the error? If manual re-validation is needed, what is the minimal composition API pattern to sync the errorBag state with the UI without a full form reset? How does validateOnMount=true interact with async rules that resolve after the component first renders, and does this affect error message formatting via mapErrors?
32775 reputation · 23 Feb 2023, 10:36 UTC
Yes, veevalidate automatically updates the UI when an async validation promise rejects after the initial mount. Because veevalidate stores validation results in reactive references (such as the errors ref provided by useField), any update to the error state—including a promise rejection—triggers Vue's reactivity system, forcing a re-render of the template where those errors are bound.
When validateOnMount: true is used in veevalidate v4, the validate() method is invoked during the component's setup phase. The timing of the UI update depends on when the promise settles:
errorBag during the initial render cycle, and the error message appears immediately.errors ref upon rejection. Vue schedules a DOM update for the next tick, and the error message surfaces in the UI without requiring a manual trigger.To ensure the UI stays in sync with the errorBag, use the useField hook to bind the field value and its corresponding error message. No manual re-validation logic is required.
import { useField } from 'vee-validate';
// Async rule returning a Promise
const { value, errorMessage } = useField('username', (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Simulate server-side rejection
reject('Username is already taken');
}, 1000);
});
});
In your template, bind directly to the errorMessage:
<input v-model="value" />
<span v-if="errorMessage">{{ errorMessage }}</span>
If the UI is not updating, verify the following assumptions:
errors array or replacing the reactive ref with a plain JavaScript object, as this breaks Vue's tracking.useField/useForm) or the v-model directive. Using non-reactive $refs in the Options API may bypass the automatic update cycle.mapErrors functions typically operate on the current state of the errorBag; since the bag is reactive, mapped errors will also update automatically when the async promise settles.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.