一般情况下(form中的组件都是element提供的组件)在使用elm的表单校验时我们是这么使用的:
// 栗子.vue
<template>
<el-form :model="formData" :rule="rules" ref="formRef">
<el-form-item prop="inputValue">
<el-input v-model="formData.inputValue"></el-input>
</el-form-item>
<el-form-item>
<el-button @click="submit">提交</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
.......省略
data() {
return {
formData: {
inputValue: ''
},
rules: {
inputValue: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
]
}
}
},
methods: {
submit() {
this.$refs.formRef.validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
}
}
}
</script>
但是当我们在<el-form-item>组件中添加自定义的组件时,你还继续按照上面这中用法是无效的,翻阅element-ui源码就能发现其中原因。
element-ui的form组件的表单验证是由<el-form-item>组件配合触发的,在el-form-item中的源码如下:
// el-form-item源码
mounted() {
if (this.prop) {
this.dispatch('ElForm', 'el.form.addField', [this]);
let initialValue = this.fieldValue;
if (Array.isArray(initialValue)) {
initialValue = [].concat(initialValue);
}
Object.defineProperty(this, 'initialValue', {
value: initialValue
});
this.addValidateEvents(); // 添加校验事件
}
},
methods: {
onFieldBlur() { // blur 事件回调
this.validate('blur'); // 触发validate
},
onFieldChange() { // change事件回调
if (this.validateDisabled) {
this.validateDisabled = false;
return;
}
this.validate('change'); // 触发validate
},
addValidateEvents() {
const rules = this.getRules();
if (rules.length || this.required !== undefined) {
this.$on('el.form.blur', this.onFieldBlur); // ****重点****:监听el.form.blur事件,执行onFieldBlur回调
this.$on('el.form.change', this.onFieldChange);
}
},
validate(trigger, callback = noop) { // 校验方法
this.validateDisabled = false;
const rules = this.getFilteredRule(trigger); // 过滤符合校验触发事件的校验对象
if ((!rules || rules.length === 0) && this.required === undefined) {
callback();
return true;
}
this.validateState = 'validating'; // 切换校验状态
const descriptor = {};
if (rules && rules.length > 0) {
rules.forEach(rule => {
delete rule.trigger; // 删除rule对象里的trigger属性,因为validator.validate的配置项里不需要trigger属性
});
}
descriptor[this.prop] = rules;
const validator = new AsyncValidator(descriptor); // 实例化校验器
const model = {};
model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, (errors, invalidFields) => { // 校验
this.validateState = !errors ? 'success' : 'error'; // 切换校验状态
this.validateMessage = errors ? errors[0].message : '';
callback(this.validateMessage, invalidFields);
this.elForm && this.elForm.$emit('validate', this.prop, !errors, this.validateMessage || null); // 向外暴露validate事件,就是element-ui form组件API文档里的validate事件
});
}
}
从源码可以看出,<el-form-item>组件触发校验的方法是validate,而这个方法需要在onFieldBlur和onFieldChange这两个回调函数里触发,而这两个函数的触发方式是通过在addValidateEvents中监听**el.form.blur和el.form.change事件来触发(源代码:this.$on('el.form.blur', this.onFieldBlur)),所以归根结底是要触发这两个事件。**
在element的el-input, el-select, el-cascader, el-checkbox等组件的源码中发现了触发校验事件的方法:
// el-input, el-select, el-cascader, el-checkbox 等组件源码(伪代码)
<script>
export default {
...省略
handleValueBlur(val) { // 组件绑定值发生变化时的回调函数,有的是触发blur事件的回调,有的是触发change事件的
...省略
this.$emit('blur', val);
this.dispatch('ElFormItem', 'el.form.blur', [val]); // 触发blur校验事件
},
handleValueChange(val) {
...省略
this.$emit('change', val);
this.dispatch('ElFormItem', 'el.form.change', [val]); // 触发change校验事件
}
}
</script>
在组件blur或change时除了发送blur和change事件以外还调用了两个dispatch方法,重点来了:this.dispatch('ElFormItem', 'el.form.blur', [val]);,理解一下dispatch这个方法吧(熟悉发布订阅的选手们对这个名词并不陌生)~ 找到它在element的emitter.js里:
// emitter.js
export default {
methods: {
dispatch(componentName, eventName, params) { // @param 1: 触发事件的组件名称 2: 事件名称 3. 额外参数
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) { // 根据componentName自下而上递归查找目标组件
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) { // 用名称为[componentName]的组件$emit事件
parent.$emit.apply(parent, [eventName].concat(params));
}
}
}
}
综上代码,得知dispatch方法是通过目标组件发布事件。 我们回归刚才的代码this.dispatch('ElFormItem', 'el.form.blur', [val]);和this.$on('el.form.blur', this.onFieldBlur); 就是ElFormItem中订阅了el.form.blur和el.form.change两个事件,想要触发校验,必须要由ElFormItem组件发布这两个事件。
所以得出结论,因为在我们自定义的组件内部没有触发el.form.blur和el.form.change这两个事件,所以想要使用 el-form, el-form-item 组件的表单校验功能,组件内部必须要用包裹它的el-form-item组件$emit el.form.blur和el.form.change。代码这么写:
// 结论栗子.vue
<template>
<el-form :model="formData" :rule="rules" ref="formRef">
<el-form-item label="内容" prop="inputValue" ref="inputValueRef"> <!-- 添加ref, 用来调用$emit -->
<my-input v-model="formData.inputValue" @blur=“handleBlur”></my-input>
</el-form-item>
<el-form-item>
<el-button @click="submit">提交</el-button>
</el-form-item>
</el-form>
</template>
<script>
import MyInput from './MyInput.vue'; // 自定义富文本组件
export default {
.......省略
components: {MyInput},
data() {
return {
formData: {
inputValue: ''
},
rules: {
inputValue: [
{ required: true, message: '请输入内容', trigger: 'blur' },
]
}
}
},
methods: {
handleBlur(v) { // 添加blur事件回调,为了emit这个'el.form.blur'事件!
this.$refs.inputValueRef.$emit('el.form.blur', v); // 重点!
},
submit() {
this.$refs.formRef.validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
}
}
}
</script>
最后就能解决自定义组件使用element表单校验的问题了。
效果图: