Vuelidate and Vuelidate-error-extractor.
Easy form validation
Dobromir Hristov
Lead FE developer @Hypefactors
Bulgaria
Team Member
Vue
- Validation without Plugins
- Validation with Vuelidate
- Custom Vuelidate Validators
- Large forms validation
- Validation message display
- Vuelidate-error-extractor
Agenda
without plugins
Validation
data: () => ({
form: {
name: "",
email: ""
},
dirty: false
}),
computed: {
hasErrors() {
return !(this.isNameValid && this.isEmailValid);
},
isNameValid() {
const name = this.form.name
return required(name) && min(name, 10);
},
isEmailValid() {
const email = this.form.email
return required(email) && isEmail(email);
}
},
methods: {
submit() {
this.dirty = true;
// return if errors
if (this.hasErrors) {
return false;
}
alert("Form submitted");
}
}
<div
class="px-4"
:class="{ hasError: !isNameValid && dirty }"
>
<label class="mr-2 font-bold text-grey">Name</label>
<input type="text" class="input" v-model="form.name" />
</div>
<div
class="px-4"
:class="{ hasError: !isEmailValid && dirty }"
>
<label class="mr-2 font-bold text-grey">Email</label>
<input type="email" class="input" v-model="form.email" />
</div>
Outcome
- Simple for small forms
- Computed props auto update the validations
- Can extract common validators
- Need to define computed props for every field
- Need to update on multiple places
- Need to keep track of dirty fields
- Error prone
Cons
Pros
using Vuelidate
Validation
- Data-model oriented
- Rules resemble data structure
- Easy to understand
- Allow dynamic rule set
- Allow Async rules
- Track dirty fields
- Has many prebuilt validators
import { required, email } from 'vuelidate/lib/validators'
export default {
data:()=>({
form: {
name: '',
email: ''
}
}),
validations: {
form: {
name: { required },
email: { required, email }
}
}
}
Outcome
- Less boilerplate
- Less repetition
- Less user errors
- Automatic dirty field tracking
Vuelidate validators
Custom
- Wide variety of prebuilt validators
- Custom validators are just functions that return Boolean or Promise<Boolean>
- Receive Value and Context as params
- required
- requiredIf
- requiredUnless
- minLength
- maxLength
- minValue
- maxValue
- between
- alpha
- alphaNum
- numeric
- integer
- decimal
- ipAddress
- macAddres
- sameAs
- url
Custom Validator
Example
export function isNotJoe(value) {
if (!value) return true;
return !value.includes("Joe");
}
export function notGmail(value = "") {
return !value || !value.includes("gmail")
}
export function illegalChars(value) {
const charsList = ["#", "&", "$", "~"];
const regex = `[${charsList.join(",")}]`;
const regexExp = new RegExp(regex);
return !regexExp.test(value);
}
import { illegalChars, isNotJoe, notGmail } from './validators'
import { required, email } form 'vuelidate/lib/validators'
export default {
data: () => ({
form: {
name: '',
email: ''
}
}),
validations: {
form: {
name: { required, illegalChars, isNotJoe },
email: { required, email, notGmail }
}
}
}
Usage
form validation
Large
Approach
- Split logical parts into sub components
- Keep data on the parent
- Pass only required data to children
- Children emit changes to parent
- Parent passes validator down to children
- Parent is responsible for submitting form
Example Child
<template>
<div
class="form-group"
:class="{ hasError: v.$error }"
>
<label class="mr-2 font-bold text-grey">
Email
</label>
<input
type="email"
class="input"
v-model="email"
placeholder="user@yahoo.com"
/>
<div class="text-sm mt-2 text-grey-darker">
Email is required and must NOT be gmail<br />
Async check if email is less than 10 chars
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ""
},
v: {
type: Object,
required: true
}
},
computed: {
email: {
get() {
return this.value;
},
set(value) {
this.v.$touch();
this.$emit("input", value);
}
}
}
};
</script>
Child Usage
<form @submit.prevent="submit" novalidate>
<div class="flex justify-center my-6">
<div class="px-4">
<name-component v-model="form.name" :v="$v.form.name"/>
</div>
<div class="px-4">
<email-component v-model="form.email" :v="$v.form.email"/>
</div>
</div>
<div class="text-center">
<button type="submit" class="button">
Submit
</button>
</div>
</form>
message display
Error
- Good for UX
- Immediate feedback
Pros
- Repetitive
- Error prone
- Time consuming
- Extra noise in templates
Cons
<div class="form-group" :class="{ 'hasError': v.$error }">
<label class="mr-2 font-bold text-grey">Email</label>
<input
type="email"
class="input"
v-model="email"
placeholder="user@yahoo.com"
@input="v.$touch()"
>
<div class="text-sm mt-2 text-red" v-if="v.$error">
<div v-if="!v.required">Email is required</div>
<div v-if="!v.notGmail">Email should not be a Gmail one</div>
<div v-if="!v.isEmailAvailable">Email is not available</div>
<div v-if="!v.email">Email is not a properly formatted</div>
</div>
</div>
Error
messages example
to the rescue
Vuelidate-error-extractor
<form-group :validator="v" label="Email">
<input
type="email"
class="input"
v-model="email"
placeholder="user@yahoo.com"
@input="v.$touch()">
</form-group>
<div class="form-group" :class="{ 'hasError': v.$error }">
<label class="mr-2 font-bold text-grey">Email</label>
<input
type="email"
class="input"
v-model="email"
placeholder="user@yahoo.com"
@input="v.$touch()"
>
<div class="text-sm mt-2 text-red" v-if="v.$error">
<div v-if="!v.required">Email is required</div>
<div v-if="!v.notGmail">Email should not be a Gmail one</div>
<div v-if="!v.isEmailAvailable">Email is not available</div>
<div v-if="!v.email">Email is not a properly formatted</div>
</div>
</div>
After
Before
Usage
import VuelidateErrorExtractor, { templates } from "vuelidate-error-extractor";
Vue.use(VuelidateErrorExtractor, {
i18n: false,
// Define common validation messages.
messages: {
required: "{attribute} is required!",
isJoe: "{attribute} must be Joe",
notGmail: "{attribute} must not be gmail",
email: "{attribute} is not a valid Email address.",
isEmailAvailable:
"{attribute} is not available. Must be at least 10 characters long."
}
});
Vue.component("form-group", templates.singleErrorExtractor.foundation6);
Installation
- Templates for Foundation, Bootstrap 3 & 4
- Predefine common reusable error messages
- Has support for Vue-i18n
- Removes repetitive work
- Auto extracts all the errors
- Obeys dirty rule check
- Simple to use
- Extendable
Pros
error component
Custom
<template>
<div class="form-group" :class="{ hasError: hasErrors, hasSuccess: isValid }">
<div class="label">
{{ label }}
</div>
<div class="control"><slot/></div>
<div class="control-helper text-red mt-4 text-sm" v-if="hasErrors">
<div v-for="error in activeErrorMessages" :key="error">{{ error }}</div>
</div>
</div>
</template>
<script>
import { singleErrorExtractorMixin } from "vuelidate-error-extractor";
export default {
mixins: [singleErrorExtractorMixin]
};
</script>
errors | Object | All validation error objects for the field. Its a dynamic property so its in sync with the form field's state. |
activeErrors | Object | The currently active error objects. |
activeErrorMessages | Object | The currently active error messages. |
mergedMessages | Object | Merges both the messages prop and the globally defined ones on init. |
firstError | Object | Returns the first error object. |
firstErrorMessage | String | Returns the first active error message. |
hasErrors | Boolean | If field has any errors. |
Available Properties
Usage
- ElementUI
- iView
- Framework7
- Vuetify
- MintUI
- MuseUI
- Quasar
- Vue Material
- BootstrapVue
- Buefy
- Vuesax
with UI Frameworks
error component
Summary
Usage & Features
- Extendable
- Can work with deeply nested validators
- Assigns fields names to error messages
- Can work with $each validator
- Bootstrap and Foundation templates included
<form-summary :validator="$v.form"/>
Find me at
dobromir-hristov
d_m_hristov
dobromir_hristov
dobromir-hristov
Dobromir Hristov
You
Thank
Easy form validation with Vuelidate and Vuelidate-error-extractor
By Dobromir Hristov
Easy form validation with Vuelidate and Vuelidate-error-extractor
- 2,831