In this paper, the target
- Learn to use elementUI’s form components
- How the component passes values
- Encapsulate the Input component, and the form component with validation
Let’s take a look at how elementUI’s form component works
Here’s the code copied from elementUI: A basic form form consists of three components: el-form, el-form-item, and el-Input
El-form: receives two parameters: Model receives data from externally transmitted forms, and rules receives verification rules
The el-form-item component displays error information about the label and display validation
3. El-input: bidirectional binding
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"> <el-form-item Label =" active name" prop="name"> <el-input V-model =" ruleform. name"></el-input> </el-form-item> </el-form>Copy the code
Encapsulating input components
Target: V-model can be used for m-input outside, and send “validation events”
1, custom components to use v-model, must :value / @input
2. When the input event is triggered, a validation event is issued to tell the parent component to validate
V-bind =”$attrs”: passes down non-props features (except class and style) bound to the component tag when calling the component. You should add inheritAttrs: False to the child component (to prevent parent-scoped feature bindings that are not recognized as props from being applied to the root element of the child component). Note: $attrs stores parts other than props
The listeners can emit(eventName) events directly on the parent component’s tag, and the listeners can notice the events on the parent component directly on the record
The two properties can handle functions and events passed by the component without writing extra code, just passing $attrs and $Listeners up and down.
<template>
<div class="m-input">
<input :value="value" @input="onInput" v-bind="$attrs">
</div>
</template>
Copy the code
<script>
export default {
inheritAttrs: false.// Avoid the top-level container inheriting attributes
props: {
value: {
type: String.default: ' '
}
// placeholder doesn't need this props because $attrs is placeholder
},
methods: {
onInput(e) {
// Notify the parent component of the value change
this.$emit('input', e.target.value);
// Notify FormItem validation
this.$parent.$emit('validate');
}
},
}
</script>
Copy the code
Encapsulates the Form-item component
Goal:
- Displays information about label and validation errors
- Leave a slot for input
- Validates a single item
<template>
<div class="m-form-item">
<i class="warn-icon" v-if="isNecessary&&label">*</i>
<label v-if="label">
<span>{{label}}</span>
</label>
<slot></slot>
<p class="warning-msg">{{warningMsg}}</p>
</div>
</template>
Copy the code
A validation library async-Validator is required for this validation. See Github for details
Import Schema from 'async-validator'; const descriptor = { name(rule, value, callback, source, options) { const errors = []; if (! /^[a-z0-9]+$/.test(value)) { errors.push(new Error( util.format('%s must be lowercase alphanumeric characters', rule.field), )); } return errors; }}; const validator = new Schema(descriptor); validator.validate({ name: 'Firstname' }, (errors, fields) => { if (errors) { return handleErrors(errors, fields); } // validation passed });Copy the code
The js part is to accept the data of the current item, and the verification rule of the current item, and finally the verification. But form-item accepts only two arguments: label and prop are strings and do not retrieve form data. Because you can nest multiple layers of forms, you can’t just pass values from parents and pass data in. At this time, it involves several ways of vUE component transmission and communication.
Component transmission and communication
Father = > the son
- Properties props
- Reference refs:
this.$refs.form.mobile='123'
- children:
this.$children[0].mobile='123'
= > the father
- Custom events:
$emit('name',123)
/@name
Brother communication
This.$parent. On (‘name’,123)/this.$parent. Emit (‘name’)
Between ye sun.
Grandparents provide data by providing
provide() {
return {formData: 123}
}
Copy the code
Inject data by inject[‘formData’]
Any two components
Use Vuex or eventBus
Return to form-item and get the outermost parent form’s data. Therefore, we need the form component to provide the current instance to the grandchild, so form-item can inject the entire form instance
import Schema from "async-validator"
data() {
return {
isNecessary: false.// Mandatory
warningMsg: ' ' // Incorrect verification information}},inject: ['from'].// The data provided by the form
props: {
label: String.prop: String
}
Copy the code
Data and rules have been taken, the following is to verify! Write the validation method as described in the Async-Validator documentation
methods: {
onValidate() {
console.log(this.prop) // Rule name for the current item
console.log(this.form) // The entire form instance: {model: received data, rules: received rules}
const descriptor = { // Get the rule description here
[this.prop]: this.from.rules[this.prop]
}
const validator = new Schema(descriptor);
return validator.validate({ [this.prop]: this.from.model[this.prop] }, (errors) = > {
if (errors) {
console.log(errors);
this.warningMsg = errors[0].message
} else {
// Clears the error message
this.warningMsg = ' '}}}})Copy the code
Finally, the validation event is listened for and the validation method is executed
mounted() {
if (JSON.stringify(this.from.rules) ! = ='{}') {
this.isNecessary = true
}
this.$on('validate'.() = > {
this.onValidate()
})
},
Copy the code
Encapsulating form components
The form component does two things: 1. Receives data and validates rules. 2. Perform global verification. Form-item does the rest, so the page structure is simple. All you need is one slot, and it’s all custom
<template>
<div>
<slot></slot>
</div>
</template>
Copy the code
The JS part deals with two things:
- Receives external data and an array of validation rules
- Verify rules globally
provide() {
return {
'from': this // Pass the entire instance directly}},props: {
model: {
type: Object.required: true
},
rules: Object
}
Copy the code
Return return for all form-items; Note: The result of async-validator return is a Promise.
Just take out all the validation tasks and execute them
methods: { validate(cb) { const tasks = this.$children .filter(item => item.prop) .map(item => item.onValidate()); Console. log(tasks); // Tasks is an array of promises // promise. all can wrap multiple promise instances into a new promise instance. Also, success and failure return different values, success returns an array of results, Promise.all(tasks).then(() => cb(true)).catch(() => cb(false)); }}Copy the code
The basic form is complete, and the usage is consistent with elementUI
When submitting the form, simply call the form’s validate() method with this.$refs to validate the entire form
<template> <div class="home"> <m-form :model="formData" :rules="rules" ref="form"> <m-form-item label=" phone number" Prop ="phone"> <m-input type="text" V-model =" formdata. phone" placeholder=" input number "/> </m-form-item> <m-form-item Label ="password" prop="password"> < M-input type="password" V-model =" formdata. password" /> </ M-form-item > <m-form-item> <div @click="submit"> Submit </div> </m-form> </div> </template> <script> import mForm from '@/components/mForm/index.vue' import mFormItem from '@/components/mForm/Item.vue' import mInput from '@/components/mInput/index.vue' export default { name: 'Home', components: { mForm, mFormItem, mInput }, data() { return { formData: { phone: '', password: '' }, rules: { phone: [ { required: True, message: 'Please input mobile phone number'}], password: [{required: true, message: 'Please input password'}]}}}, methods: { submit() { this.$refs.form.validate((valid) => { console.log(valid); if (valid) { alert('submit! '); } else { alert('error! '); return false; }}); } }, } </script>Copy the code