乐趣区

关于vue.js:Vue自定义组件使用Elementui表单校验

个别状况下 (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,而这个办法须要在onFieldBluronFieldChange这两个回调函数里触发,而这两个函数的触发形式是通过在 addValidateEvents 中监听 el.form.blurel.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.blurel.form.change两个事件,想要触发校验,必须要由 ElFormItem 组件公布这两个事件。

所以得出结论,因为在咱们自定义的组件外部没有触发 el.form.blurel.form.change这两个事件,所以想要应用 el-form, el-form-item 组件的表单校验性能,组件外部必须要用包裹它的 el-form-item 组件 $emit el.form.blurel.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 表单校验的问题了。

效果图

退出移动版