Form validation is an important part of any app.
In this article, we’ll look at how to use Vee-Validate 4 in our Vue 3 app for form validation.
Async Validation
We can validate form fields asynchronously with Vee-Validate 4.
For example, we can write:
<template>
<div>
<Form @submit="onSubmit">
<label for="email">Email</label>
<Field id="email" name="email" :rules="validateEmail" type="email" />
<ErrorMessage name="email" />
<button type="submit">Submit</button>
</Form>
</div>
</template>
<script>
import { Field, Form, ErrorMessage } from "vee-validate";
const mockApiRequest = (value) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(value === "abc@abc.com");
}, 1000);
});
};
export default {
name: "App",
components: {
Field,
Form,
ErrorMessage,
},
methods: {
onSubmit(values) {
alert(JSON.stringify(values, null, 2));
},
async validateEmail(value) {
const result = await mockApiRequest(value);
return result ? true : "This email is already taken";
},
},
};
</script>
We have the mockApiRequest
function that returns a promise to simulate async validation.
We use that in the validateEmail
method.
If it resolves to true
, then the field is valid.
Otherwise, it’s not valid.
Then we pass the method into the rules
prop to do the validation.
When we press Submit, validation will be run.
If it’s valid, then the onSubmit
method will be run.
Cross-Field Validation
We can add cross-field validation with Vee-Validate 4.
For example, to check if the confirm password field matches the password field, we can write:
<template>
<div>
<Form @submit="onSubmit" :validation-schema="schema">
<div>
<label for="password">Password</label>
<Field id="password" name="password" type="password" />
<ErrorMessage name="password" />
</div>
<div>
<label for="passwordConfirmation">Confirm Password </label>
<Field
id="passwordConfirmation"
name="passwordConfirmation"
type="password"
/>
<ErrorMessage name="passwordConfirmation" />
</div>
<button type="submit">Submit</button>
</Form>
</div>
</template>
<script>
import { Field, Form, ErrorMessage, defineRule } from "vee-validate";
import * as yup from "yup";
defineRule("required", (value) => {
if (!value) {
return "This is required";
}
return true;
});
defineRule("min", (value, [min]) => {
if (value && value.length < min) {
return `Should be at least ${min} characters`;
}
return true;
});
defineRule("confirmed", (value, [other]) => {
if (value !== other) {
return `Passwords do not match`;
}
return true;
});
export default {
name: "App",
components: {
Field,
Form,
ErrorMessage,
},
data: () => {
const schema = yup.object().shape({
password: yup.string().min(5).required(),
passwordConfirmation: yup
.string()
.required()
.oneOf([yup.ref("password")], "Passwords do not match"),
});
return {
schema,
};
},
methods: {
onSubmit(values) {
alert(JSON.stringify(values, null, 2));
},
},
};
</script>
We called defineRule
to define the required
and min
rules to check that the field has a value filled in and that it has the minimum length required.
The confirmed
rule is defined with the other
variable to check the value of the other field.
We define the Yup schema in the data
method.
The validation rules for the passwordConfirmation
rule calls oneOf
with yup.ref("password")
to check that the value of it matches the value of the password
field.
Then we pass in the schema
into the validation-schema
prop.
Conclusion
We can validate form fields asynchronously and do cross-field validation in our Vue 3 app with Vee-Validate 4.