el-form嵌套为何会导致频繁触发外层表单校验?

2,033 阅读1分钟

问题描述

整个页面是个大的表单,里面嵌套了多个子组件,最终在保存时在父页面统一校验。出现的问题是如果子组件内部相关元素发生变化时会触发外层校验,但这并不是想要的效果,想要的效果是只有最终在点击保存的时候再去触发校验。示例代码如下:

// parent.vue
<template>
  <el-form :rules="rules" :form="form" ref="form">
    <el-form-item prop="name" label="组件1">
      <child-component v-model="form.name"></child-component>
    </el-form-item>
    ... ...
    <el-button @click="save">保存</el-button>
  </el-form>
</template>

<script>
import ChildComponent from './child.vue'
export default {
  components: {
    ChildComponent
  },
  data () {
    return {
      form: {
        name: ''
      },
      rules: {
        name: [
          { required: true, message: '请选择', trigger: 'change' }
        ]
      }
    }
  },
  methods: {
    // 最后保存时进行整体校验
    save () {
       this.$refs['form'].validate()
    }
  }
}
</script>

子组件内部主要是通过一定的查询条件调用接口得到相应的数据,通过手动勾选查询后的数据,暴露给父页面。

// child.vue
<template>
  <div>
      <!-- 筛选条件 -->
    <el-select v-model="form.value" clearable>
        <el-option label="电脑" value="1"></el-option>
    </el-select>
    <el-button @click="search">查询</el-button>
      <!-- 表格数据 -->
     <el-table 
        border 
        :data="tableData"
        @selection-change="handleSelectionChange">
        <el-table-column
            type="selection"
            width="55">
        </el-table-column>
        <el-table-column 
            prop="name" 
            label="名称">
        </el-table-column>
        ... ...
     </el-table>
    </div>
</template>
<script>
export default {
  data () {
    return {
      tableData: [],
      form: {
        value: ''
      }
    }
  },
  methods: {
    // 数据查询
    search () {},
    // 把勾选的数据暴露给父页面
    handleSelectionChange (val) {
        this.$emit('input', val)
    }
  }
}
</script>

ElSelect数据发生改变后会触发外层的校验,清空下拉框选项后会出现错误提示语。

image.png

原因

通过查看Element相关源码,可以看到对于ElSelect组件内部监听了其value值,一旦发生变化就会触发ElFormItem的校验方法。

image.png

image.png 其实不止是ElSelect组件,还有ElRadioGroupElCheckBox等等组件也存在同样的情况,本文就不一一列举了,感兴趣的大家可以自行查看。

解决方案

方案一(推荐)父页面改变DOM结构

把子组件拿到外层,这样的话组件内部无论怎么操作都不会触发ElFormItem的校验。但需要保留相关ElFormItem,因为保存时,如果未通过校验需要展示错误提示语。

// parent.vue
<template>
  <el-form :rules="rules" :form="form" ref="form">
    <child-component v-model="form.name"></child-component>
    <el-form-item prop="name" label="组件1"></el-form-item>
    ... ...
  </el-form>
</template>

方案二、子组件内部嵌套ElFormItem,切断校验触发

// child.vue
<template>
    /* 嵌套el-form-item */
    <el-form-item>
      <el-select v-model="form.value" clearable>
            <el-option label="电脑" value="1"></el-option>
        </el-select>
        <el-button @click="search">查询</el-button>
          <!-- 表格数据 -->
         <el-table 
            border 
            :data="tableData"
            @selection-change="handleSelectionChange">
            <el-table-column
                type="selection"
                width="55">
            </el-table-column>
            <el-table-column 
                prop="name" 
                label="名称">
            </el-table-column>
            ... ...
         </el-table>
    </el-form-item>
</template>

image.png 通过以上方案,保证子组件只负责抛出数据,并不会触发校验操作。