鍍金池/ 問答/HTML5  HTML/ 關(guān)于ElementUI 表單值類型檢測(cè)失敗的問題

關(guān)于ElementUI 表單值類型檢測(cè)失敗的問題

剛剛開始接觸Vue以及ElementUI,最近在開發(fā)過程中遇到了問題,經(jīng)歷了各種痛苦的debug....直接入正題把:

這里好像不可以傳文件,我就直接貼代碼了:

<template>
    <div>
        <el-button @click="onSelect">點(diǎn)我</el-button>

        <el-dialog
        title="彈窗"
        :visible.sync="dialogVisible"
        width="50%">
            <el-form 
            ref="testForm" 
            :model="formData" 
            :rules="formRules">
                <el-form-item label="類型">
                    <el-select v-model="formData.class">
                        <el-option label="類型1" value="class1"></el-option>
                        <el-option label="類型2" value="class2"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="class1" v-if="formData.class=='class1'" prop="text">
                    <el-input type="textarea" v-model="formData.text"></el-input>
                </el-form-item>
                <el-form-item label="類型2" v-else prop="value">
                    <el-select v-model="formData.value" multiple>
                        <el-option label="sdf" value="11"></el-option>
                        <el-option label="dsaf" value="22"></el-option>
                        <el-option label="dyrt" value="33"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="onSubmit">ok</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>
    </div>
</template>

<script>
export default {
    data(){
        return {
            dialogVisible: false,
            formData: {
                class: '',
                text: '',
                value: []
            },
            formRules: {
                text: [{
                    required:true,
                    validator(rule, value, callback){
                        var Reg = /[\w+\/]*\w+/;
                        if(value==''){
                            callback(new Error('該字段不能為空'))
                        }else if(!value.match(Reg) || value.match(Reg)!=value){
                            callback(new Error('請(qǐng)輸入正確的用戶名,用戶名之間用/隔開'));
                        }else{
                            callback();
                        }
                    }
                }],
                value: [{
                    required:true,
                    message:'不能為空'
                }]
            }
        }
    },
    methods: {
        onSubmit(){},
        onSelect(){
            this.dialogVisible = true;
            this.$nextTick(() => {
                this.$refs.testForm.resetFields();
            });
            console.log(this.formData)
        }
    }
}
</script>

這個(gè)組件就是一個(gè)按鈕,點(diǎn)擊按鈕會(huì)出現(xiàn)一個(gè)彈窗,圖如下:

clipboard.png

由于是提問題,盡量省去了無關(guān)代碼,包括自定義組件以及css代碼,所有的元素都由ElementUI提供。

問題重現(xiàn)步驟:
1.點(diǎn)開彈窗,選擇類型為類型1,此時(shí)文本輸入域會(huì)變?yōu)閠extarea,關(guān)閉彈窗。
2.再次點(diǎn)擊按鈕打開彈窗,控制臺(tái)會(huì)報(bào)錯(cuò),類型檢測(cè)錯(cuò)誤。

debug歷程如下:
1.刪掉el-form-item組件的prop屬性,則不會(huì)報(bào)錯(cuò)。是表單數(shù)據(jù)對(duì)象檢測(cè)錯(cuò)誤。
2.去掉值表單驗(yàn)證也不會(huì)報(bào)錯(cuò),證明數(shù)據(jù)對(duì)象檢測(cè)在表單驗(yàn)證階段出錯(cuò)。
3.確定自己定義的表單初始值類型并沒有什么問題。
4.最后鎖定問題出現(xiàn)在打開彈窗之后的重置表單方法resetFields
5.在resetFields方法之后輸出表單數(shù)據(jù),圖如下

clipboard.png

初始定義的表單數(shù)據(jù)如下

clipboard.png

求各位大佬能給我解解惑,為啥我定義的text值為String,在經(jīng)過表單之后會(huì)變?yōu)锳rray,究竟是我使用不規(guī)范還是ElementUI本身就認(rèn)為textarea的字段值為Array亦或是其他問題,望指正。小弟在此拜謝各位大佬!

回答
編輯回答
只愛你

v-if/v-else改成v-show。

https://jsfiddle.net/s5ar1un3...

我也才發(fā)現(xiàn)有這種坑,原理后面慢慢說。

以下為element-ui源碼

form.vue

//created階段
created() {
      this.$on('el.form.addField', (field) => {
        if (field) {
          this.fields.push(field);
        }
      }
      
      this.$on('el.form.removeField', (field) => {
        if (field.prop) {
          this.fields.splice(this.fields.indexOf(field), 1);
        }

      });
}

//重置方法
resetFields() {
        ...
        
        this.fields.forEach(field => {
          field.resetField();
        });
      },

form-item.vue

//mounted階段
    mounted() {
        if (this.prop) {
            this.dispatch('ElForm', 'el.form.addField', [this]);
            ...
            
            let initialValue = this.fieldValue;
            
            ...
            
            Object.defineProperty(this, 'initialValue', {
              value: initialValue
            });
            ...
        }
    }

//重置方法
    resetField() {

        this.validateDisabled = true;
        if (Array.isArray(value)) {
          prop.o[prop.k] = [].concat(this.initialValue);
        } else {
          prop.o[prop.k] = this.initialValue;
        }
    }

從源碼可以看出,在form-itemmounted階段確定每個(gè)field的初始值。

創(chuàng)建階段

由于v-if/v-else,因此text所在組件創(chuàng)建沒有prop(這時(shí)候創(chuàng)建了3個(gè)item組件,class本身也沒寫prop情況會(huì)一樣),因此只有value所在item組件創(chuàng)建的時(shí)候觸發(fā)了form組件的el.form.addField事件。所以保存的fields長(zhǎng)度為1,僅保存了value這個(gè)組件。

選中之后
由于數(shù)據(jù)變動(dòng),vue比較差異更新,然后更新了第2個(gè)組件(這是重點(diǎn)),然鵝this.initialValueelement-ui定義的值,還是原來value的值。

結(jié)果就是
resetField的時(shí)候value.initialValue的值賦給了text。

結(jié)論

所以resetField千萬別用v-if/v-show除非你確認(rèn)過上面的邏輯跟你的業(yè)務(wù)邏輯不沖突。
測(cè)試版本element-ui@2.3.3

補(bǔ)充

以上都是不看文檔的結(jié)果(花式打臉),vue提供了:key阻止組件復(fù)用。Vuejs

Vue 會(huì)盡可能高效地渲染元素,通常會(huì)復(fù)用已有元素而不是從頭開始渲染。這么做除了使 Vue 變得非??熘猓€有其它一些好處。例如,如果你允許用戶在不同的登錄方式之間切換

這樣也不總是符合實(shí)際需求,所以 Vue 為你提供了一種方式來表達(dá)“這兩個(gè)元素是完全獨(dú)立的,不要復(fù)用它們”。只需添加一個(gè)具有唯一值的 key 屬性即可:

注意如validate組件不存在不驗(yàn)證一樣,resetField也不reset。

2017年1月17日 14:20
編輯回答
獨(dú)白

被題主坑死,不按你debug歷程,這問題早解決了。。話不多說,上代碼:

    <template>
    <div>
        <el-button @click="onSelect">點(diǎn)我</el-button>

        <el-dialog
                title="彈窗"
                :visible.sync="dialogVisible"
                width="50%">
            <el-form
                    ref="formData"
                    :model="formData"
                    :rules="formRules">
                <el-form-item label="類型">
                    <el-select v-model="formData.Type">
                        <el-option label="類型1" value="class1"></el-option>
                        <el-option label="類型2" value="class2"></el-option>
                    </el-select>
                </el-form-item>
                <div v-if="formData.Type=='class1'">
                    <el-form-item label="class1" prop="desc">
                        <el-input type="textarea" v-model="formData.desc"></el-input>
                    </el-form-item>
                </div>
                <div v-else>
                    <el-form-item label="類型2" prop="region">
                        <el-select v-model="formData.region" multiple>
                            <el-option label="sdf" value="11"></el-option>
                            <el-option label="dsaf" value="22"></el-option>
                            <el-option label="dyrt" value="33"></el-option>
                        </el-select>
                    </el-form-item>
                </div>
                <el-form-item>
                    <el-button type="primary" @click="onSubmit('formData')">ok</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>
    </div>
</template>

<script>
    export default {
        data(){
            return {
                dialogVisible: false,
                formData: {
                    Type: 'class1',
                    desc: '',
                    region: []
                },
                formRules: {
                    desc: [
                        {
                            required:true,
                            validator(rule, value, callback){
                                var Reg = /[\w+\/]*\w+/;
                                if(value==''){
                                    callback(new Error('該字段不能為空'))
                                }else if(!value.match(Reg) || value.match(Reg)!=value){
                                    callback(new Error('請(qǐng)輸入正確的用戶名,用戶名之間用/隔開'));
                                }else{
                                    callback();
                                }
                            }
                        }
                    ],
                    region: [
                        { required:true, message:'不能為空', trigger: 'change' }
                    ]
                }
            }
        },
        methods: {
            onSubmit(formName){
                this.$refs[formName].validate((valid) => {
                    if (valid) {
                        this.dialogVisible = false;
                        alert('submit!');
                    } else {
                        console.log('error submit!!');
                        return false;
                    }
                });
            },
            onSelect(){
                this.dialogVisible = true;
                this.$nextTick(() => {
                    this.$refs.formData.resetFields();
                    console.log(this.Type)
                });
                console.log(this.formData)
            }
        }
    }
</script>

你的問題就是v-if/v-else這里,因?yàn)槟愠跏糡ype為' ',v-if="formData.Type=='class1'",所以你才顯示的是textarea這塊。但是你第二次點(diǎn)擊,執(zhí)行resetFields后,Type為undefined,顯示卻為select這塊,desc的類型會(huì)因?yàn)橹刂米兊母鷕egion一樣,為Array;這也解釋了為什么你第一次選擇類型2,第二次點(diǎn)擊進(jìn)來,不會(huì)報(bào)錯(cuò)。

2018年7月13日 22:16