Form validation is a difficult subject in frontend, not only you have to deal with ensuring correct values are submitted, you also should provide a pleasant UX for your users.
Doing form validation by hand is a lot of work and you won't probably cover all your needs if you decide to build your own.
The time you spend working on a custom form validation solution is better spent building your application logic.
Most validation libraries will save you a lot of time, but vee-validate tackles the major pain points of form validation and addresses them in a very flexible way:
You can define validation rules for your fields using the useField composition API function, your rules can be as simple as a function that accepts the current value and returns an error message.
<template>
<input
v-model="value"
type="text"
class="form-control form-control-solid"
id="example1VeeValidate"
placeholder="Interact with this field"
/>
<span class="text-danger">{{ errorMessage }}</span>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { useField } from "vee-validate";
export default defineComponent({
name: "example-1",
setup() {
const isRequired = (value) => {
if (value && value.trim()) {
return true;
}
return "This is required";
};
const { errorMessage, value } = useField("fieldName", isRequired);
return {
errorMessage,
value,
};
},
});
</script>
The validation happens automatically when value binding changes, meaning you can use useField to trigger validation for any kind of data and not just for inputs.
yup is a very popular, simple and powerful data validation library for JavaScript, you can use it in combination with vee-validate, You can use yup to define your validation rules for that field:
<template>
<input
v-model="value"
type="text"
class="form-control form-control-solid"
id="example1VeeValidate"
placeholder="Interact with this field"
/>
<span class="text-danger">{{ errorMessage }}</span>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { useField } from "vee-validate";
import * as yup from "yup";
export default defineComponent({
name: "example-2",
setup() {
const { errorMessage, value } = useField(
"fieldName",
yup.string().required().min(8).label("Value"),
);
return {
value,
errorMessage,
};
},
});
</script>
Instead of providing validations for each field individually which can clutter your code, you can instead define the validation schema using the useForm function by passing a validationSchema option. Each field will automatically be associated with it and will be validated accordingly.
A simple validation schema can be an object containing field names as keys and validation functions as the value for those keys.
<template>
<div class="mb-10">
<label for="emailField" class="form-label">Email address</label>
<input
type="email"
name="email"
v-model="email"
class="form-control form-control-solid"
id="emailField"
placeholder="email"
/>
<span class="text-danger">{{ emailError }}</span>
</div>
<label for="passwordField" class="form-label">Password</label>
<input
type="password"
name="password"
v-model="password"
class="form-control form-control-solid"
id="passwordField"
placeholder="password"
/>
<span class="text-danger">{{ passwordError }}</span>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { useField, useForm } from "vee-validate";
export default defineComponent({
name: "example-3",
setup() {
// Define a validation schema
const simpleSchema = {
email(value) {
if (value && value.trim()) {
return true;
}
return "Email is required";
},
password(value) {
if (value && value.trim()) {
return true;
}
return "Password is required";
},
};
// Create a form context with the validation schema
useForm({
validationSchema: simpleSchema,
});
// No need to define rules for fields
const { value: email, errorMessage: emailError } = useField("email");
const { value: password, errorMessage: passwordError } =
useField("password");
return {
email,
emailError,
password,
passwordError,
};
},
});
</script>
Fortunately there is already a very neat way to build validation schemas for your forms by using yup, it allows you create validation objects like this:
vee-validate has built-in support for yup schemas, You can pass your schemas to the useForm function using the same validationSchema option:
<template>
<label for="emailField" class="form-label">Email address</label>
<input
type="email"
name="email"
v-model="email"
class="form-control form-control-solid"
id="emailField"
placeholder="email"
/>
<span class="text-danger">{{ emailError }}</span>
<label for="passwordField" class="form-label">Password</label>
<input
type="password"
name="password"
v-model="password"
class="form-control form-control-solid"
id="passwordField"
placeholder="password"
/>
<span class="text-danger">{{ passwordError }}</span>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import * as yup from "yup";
import { useForm, useField } from "vee-validate";
export default defineComponent({
name: "example-4",
setup() {
// Define a validation schema
const schema = yup.object({
email: yup.string().required().email(),
password: yup.string().required().min(8),
});
// Create a form context with the validation schema
useForm({
validationSchema: schema,
});
// No need to define rules for fields
const { value: email, errorMessage: emailError } = useField("email");
const { value: password, errorMessage: passwordError } =
useField("password");
return {
email,
emailError,
password,
passwordError,
};
},
});
</script>