博客 / 詳情

返回

Vue自定義組件使用Element-ui表單校驗

一般情況下(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表單校驗的問題了。

效果圖

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.