Form validation is a common feature in front-end development. Some UI libraries such as Ant. Design and Element UI implement Form components with validation. Async-validator is a library for asynchronous validation of data. Ant. Design and Element UI Form components use async-Validator. This article introduces the basic usage of async-Validator and uses it to implement a simple Form component with validation.
1. Basic usage of Async-Validator
The async-Validator function verifies whether data is valid and presents a prompt according to the verification rule.
Let’s demonstrate the most basic use of async-Validator.
import AsyncValidator from 'async-validator'
// Check rules
const descriptor = {
username: [{required: true.message: 'Please fill in user name'
},
{
min: 3.max: 10.message: 'Username length 3-10'}}]// Construct a validator based on the validation rules
const validator = new AsyncValidator(descriptor)
const data = {
username: 'username'
}
validator.validate(model, (errors, fields) => {
console.log(errors)
})
Copy the code
When data does not conform to the validation rules, the corresponding error message is displayed in the validator.validate callback function.
When the common verification rules in async-Validator cannot meet the requirements, we can write a customized verification function to verify data. A simple check function is as follows.
function validateData (rule, value, callback) {
let err
if (value === 'xxxx') {
err = 'Not conforming to specification'
}
callback(err)
}
const descriptor = {
complex: [{validator: validateData
}
]
}
const validator = new AsyncValidator(descriptor)
Copy the code
The Async-Validator supports asynchronous validation of data. Therefore, when writing a custom validation function, callback is called regardless of whether the validation is passed.
2. Write the Form and FormItem components
Now that you know how to use async-Validator, how to combine the library with the Form component you’re writing.
Implementation approach
Use a diagram to illustrate the implementation idea.
The Form component
The Form component should be a container that contains an indefinite number of FormItems and other elements. You can use the slot component built into Vue to represent the contents of the Form.
The Form component also needs to know how many FormItem components it contains to validate. Normally, parent and child components communicate by binding events to the child component, but with slot, you cannot listen for the child component’s events. Here you can listen for events on the Form component via $on and fire custom events on the Form component before the FormItem is mounted or destroyed.
With that in mind, we’ll start by writing the Form component.
<template>
<form class="v-form">
<slot></slot>
</form>
</template>
<script>
import AsyncValidator from 'async-validator'
export default {
name: 'v-form',
ComponentName: 'VForm', // use $options.componentName to find the form component
data () {
return {
Fields: [], // field: {prop, el}, save FormItem information.
formError: {}
}
},
computed: {
formRules () {
const descriptor = {}
this.fields.forEach(({prop}) => {
if (! Array.isArray(this.rules[prop])) {
Console. warn(' prop ${prop} FormItem check rule does not exist or its value is not an array ')
descriptor[prop] = [{ required: true }]
return
}
descriptor[prop] = this.rules[prop]
})
return descriptor
},
formValues () {
return this.fields.reduce((data, {prop}) => {
data[prop] = this.model[prop]
return data
}, {})
}
},
methods: {
validate (callback) {
const validator = new AsyncValidator(this.formRules)
validator.validate(this.formValues, (errors) => {
let formError = {}
if (errors && errors.length) {
errors.forEach(({message, field}) => {
formError[field] = message
})
} else {
formError = {}
}
this.formError = formError
// Make the order of the error messages the same as that of the form components
const errInfo = []
this.fields.forEach(({prop, el}, index) => {
if (formError[prop]) {
errInfo.push(formError[prop])
}
})
callback(errInfo)
})
}
},
props: {
model: Object,
rules: Object
},
created () {
this.$on('form.addField', (field) => {
if (field) {
this.fields = [...this.fields, field]
}
})
this.$on('form.removeField', (field) => {
if (field) {
this.fields = this.fields.filter(({prop}) => prop ! == field.prop)
}
})
}
}
</script>
Copy the code
FormItem components
For the FormItem component, it’s much easier to navigate up to the Form component that contains it. You can then calculate the corresponding error message based on formError.
<template>
<div class="form-item">
<label :for="prop" class="form-item-label" v-if="label">
{{ label }}
</label>
<div class="form-item-content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: 'form-item',
computed: {
form () {
let parent = this.$parent
while (parent.$options.componentName ! == 'VForm') {
parent = parent.$parent
}
return parent
},
fieldError () {
if (! this.prop) {
return ''
}
const formError = this.form.formError
return formError[this.prop] || ''
}
},
props: {
prop: String,
label: String
}
}
</script>
Copy the code
FormItem also fires custom events for the Form component in mounted and beforeDestroy hooks.
<script>
export default {
// ...
methods: {
dispatchEvent (eventName, params) {
if (typeof this.form ! == 'object' && ! this.form.$emit) {
Console. error('FormItem must be inside the Form component ')
return
}
this.form.$emit(eventName, params)
}
},
mounted () {
if (this.prop) {
this.dispatchEvent('form.addField', {
prop: this.prop,
el: this.$el
})
}
},
beforeDestroy () {
if (this.prop) {
this.dispatchEvent('form.removeField', {
prop: this.prop
})
}
}
}
</script>
Copy the code
Finally, create a new index.js to export the written component.
import VForm from './Form.vue'
import FormItem from './FormItem.vue'
export {
VForm,
FormItem
}
Copy the code
3. Usage
The validation function for the Form is in the Form component. With $ref, you can access the Form component, call the validate function, and obtain the corresponding validation information.
The usage method is as follows:
<template>
<v-form :model="formData" :rules="rules" ref="form">
<form-item label=" prop "="tel">
<input type="tel" maxlength="11" v-model.trim="formData.tel"/>
</form-item>
<button @click="handleSubmit"> save </button>
</v-form>
</template>
<script>
import { VForm, FormItem } from './common/Form'
export default {
data () {
return {
formData: {
tel: ''
},
rules: {
tel: [
{required: true, message: 'Your mobile phone number was not entered '},
{the pattern: / ^ 1 [34578] \ d {9} $/, the message: 'your phone number input errors'}
]
}
}
},
methods: {
handleSubmit () {
this.$refs.form.validate(errs => {
console.log(errs)
})
}
},
components: {
VForm,
FormItem
}
}
</script>
Copy the code
For the full code click here.
4. To summarize
This article briefly introduces the use of async-Validator and implements a Form component with validation. There are a number of weaknesses in the Form implementation :(1) validation is performed only when the Form is submitted. (2) The FormItem component should also adjust the UI according to the verification results and give corresponding prompts. Therefore, the Form component is better suited for use on mobile devices where there is less interaction.
You can follow this implementation idea, according to the application scenario, write more customized Form components.
The resources
- async-validator
- The Form component of Element UI
- Element UI Form source code