1.问题
- validate是怎么触发验证的?是在form上校验的还是在form-item上检验的?
- 为什么rules里面的trigger可以触发相应的校验
- 为什么自定义组件如i-input可以使用v-model指定
2.思维图(组件关系)
(丑一点别介意)
3.源码分析
文件:src\components\form\form.vue
props: {
model: {
type: Object
},
// 接收校验规则
rules: {
type: Object
}
},
provide () {
// 将当前组件传递给子孙组件
return { FormInstance : this };
},
methods: {
// 校验方法
validate(callback) {
return new Promise(resolve => {
let valid = true;
let count = 0;
// fields 为空需要返回promise
if (this.fields.length === 0) {
resolve(valid);
if (typeof callback === 'function') {
callback(valid);
}
}
// 遍历fields校验,相当于调用每一个form-item上的校验方法,都通过则通过,否则不通过
this.fields.forEach(field => {
field.validate('', errors => {
if (errors) {
valid = false;
}
if (++count === this.fields.length) {
resolve(valid);
if (typeof callback === 'function') {
callback(valid);
}
}
});
});
});
}
},
created () {
// 监听form派发的on-form-item-add事件,将其添加到自己的fields里面
this.$on('on-form-item-add', (field) => {
if (field) this.fields.push(field);
return false;
});
}
可以看到form里面主要做了3件事:
- 接收校验规则rules
- 将当前组件传递给子孙组件(这里主要方便后续form-item从form的rules里面获取自己对应的规则)
- 将form-item绑定到自己fields上,方便校验的时候遍历每一个form-item的校验器
- 添加校验器,供用户确定表单的时候调用(其实调用的是form-item的校验器,解决第一个问题)
文件: src\components\form\form-item.vue
props: {
// 通过prop从form的rules上取校验规则
prop: {
type: String
},
required: {
type: Boolean,
default: false
},
rules: {
type: [Object, Array]
}
},
// 获得form组件
inject: ['FormInstance'],
mounted () {
if (this.prop) {
// 派发on-form-item-add方法,将自己传递出去,form通过on-form-item-add监听
this.dispatch('iForm', 'on-form-item-add', this);
// 给当前子组件绑定规则,并监听插槽组件派发的事件,给当前组件在相应的事件触发绑定校验方法
this.setRules();
}
},
methods: {
setRules() {
// 监听插槽组件如input派发的事件,如blur,change,并调用自己的检验方法
this.$on('on-form-blur', this.onFieldBlur);
this.$on('on-form-change', this.onFieldChange);
},
getRules () {
// 从form上获取rules
let formRules = this.FormInstance.rules;
const selfRules = this.rules;
formRules = formRules ? formRules[this.prop] : [];
return [].concat(selfRules || formRules || []);
},
// 筛选相应类型的校验规则,如trigger为blur或者为changede
getFilteredRule (trigger) {
const rules = this.getRules();
return rules.filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1);
},
// 添加校验器,trigger表示要校验的规则类型,没有则校验全部
validate(trigger, callback = function () {}) {
let rules = this.getFilteredRule(trigger);
if (!rules || rules.length === 0) {
// ...
}
let descriptor = {};
descriptor[this.prop] = rules;
// 检验器,调用三方库async-validator
const validator = new AsyncValidator(descriptor);
let model = {};
model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, errors => {
this.validateMessage = errors ? errors[0].message : '';
callback(this.validateMessage);
});
},
onFieldBlur() {
// 插槽组件失去焦点触发事件,校验trigger为blur的规则
this.validate('blur');
},
onFieldChange() {
// 插槽组件改变触发事件,校验trigger为change的规则
this.validate('change');
}
}
- 接收prop、rules等
- inject获得form
- mounted里面派发on-form-item-add方法,将自己传递出去,form通过on-form-item-add监听
- 监听插槽组件如input派发的事件,如blur,change,并调用自己的检验方法:如:this.validate('blur')(解决第二个问题)
- 添加校验器validate(trigger,callback),根据参数trigger获取相应规则,调用三方库async-validator检验
文件:src\components\input\input.vue
<template>
<input
:value="currentValue"
@blur="handleBlur"
@input="handleInput"
@change="handleChange">
</template>
methods: {
handleBlur (event) {
this.$emit('on-blur', event);
if (!findComponentUpward(this, ['DatePicker', 'TimePicker', 'Cascader', 'Search'])) {
// 派发on-form-blur事件,供form-item监听,触发相应校验
this.dispatch('FormItem', 'on-form-blur', this.currentValue);
}
},
handleInput (event) {
// ...
let value = event.target.value;
if (this.number && value !== '') value = Number.isNaN(Number(value)) ? value : Number(value);
// 这里主要是为了让input这个自定义组件可以实现v-model的双向绑定
this.$emit('input', value);
// ...
},
}
- 失去焦点的时候派发on-form-blur事件,供form-item监听,触发相应校验(解决第二个问题) 第三个问题参照官网 (以上为自己的学习记录,如有错误,欢迎指正)