【踩坑记】蠢哭,我接连两次掉到了ElementUI的el-form表单组件的坑里!

7,763 阅读2分钟

elementUI是我们前端常用的UI框架,在使用它的过程中,或多或少都会踩一些坑,有的坑是我们自己代码失误造成的,有的是组件自身的问题。

这次,在同一个项目中,我接连两次掉坑,居然都是因为自身代码问题,也是醉了。以下就是我此次掉坑的全记录,希望大家不要重复踩坑。

一、千万不要用formData = {} 来暴力清空 el-form表单数据

通常,我们在关闭表单弹框之前,都会清除表单数据(这里说的清除,其实是表单重置,让其恢复到初始状态)。这样做的目的有两个:

1.避免再打开弹框时,上一次填写的内容还在。

2.避免上一次被赋值的数据,错误地传给了后端。

一般情况下,我们会这样处理:

export default {
    data() {
        return {
            formData: {
                departId: '',//与prop绑定
                departName: '',
            }
        }
    }
    methods:{
      handleClose() {
        this.$refs.form.resetFields(); // 关键代码
        this.isVisible = false;
      },
    }
}

但大家都知道,表单有时候会因项目需求,写的很复杂,总有几个数据无法通过this.$refs.form.resetFields()来清除。

比如,某个属性,为了统一管理,我们在formData里声明了,但不能写在el-form-itemprop属性里,如上面代码中的departName属性,这时,我们通过this.$refs.form.resetFields()就无法清除该属性的值。

所以就出现了formData = {} 这种省事且暴力的写法。

一开始,也没发现问题,但多操作几次表单,就会出现表单校验异常、表单不能正常填写等问题。因为formData中的属性被暴力清除了,导致el-form的响应出现问题。

正确的做法是:

先用ElementUi提供的方法统一清除,如下:

this.$refs.form.resetFields(); 

不能清除的,则如下逐个清除:

this.formData.departName = ''

二、千万不要让resetFields() 与formData提交处于同步任务中

一般情况下,我们都是走后端接口来提交formData,然后等接口请求成功后,再用resetFields()来让表单恢复到初始状态,最后关闭弹框。

一直以来,我也是这样做的,但是最近在写一个表单时,就出现了问题。

场景是这样的:

先写一个弹框表单,然后将填写好的表单数据回显在一个表格中,等表格内容确认无误后,再将表格数据一起提交给后端接口。

关键代码如下:

export default {
    data() {
        return {
            formData: {
                departId: '', // 与prop绑定
                departName: '',
                nodeName: ''// 与prop绑定
            }
        }
    }
    methods:{
      handleClose() {
        this.$refs.form.resetFields(); 
        this.isVisible = false;
      },
      handleOk() {
           this.$refs.form.validate((isPass, val) => {
               if(isPass) {
                    this.$emit('handleAddOk', this.formData)
                    this.handleClose()
               }
               
           })
      }
    }
}

等所有代码写完之后,departName可以在表格中正常回显,但是nodeName这个字段的值,怎么也回显不了在表格中。

排查了一下,nodeName在执行handleOk这个方法时,被清空了。

怎么就被清空了呢?

原来,resetFields()清空的表单对象与this.$emit提交的formData都属于同一个堆内存对象,当用resetFields()清空了表单对象时,也就影响了this.$emit提交的formData

注意,以下写法,也会存在上述问题,因为依旧是同一个堆内存对象:

this.formData.departId = ''
this.formData.departName = ''
this.formData.nodeName = ''

所以最终改成了以下写法:

this.formData = {
    departId: '',
    departName: '',
    nodeName: ''
}

也就是,将formData重新赋值,变成了一个新对象。这样,上述问题就完美解决了。

============ 2023年4月19日更新 ============

经评论区的掘友提醒,可以用this.$options.data()来重置数据,我实践了一下,的确可以,故此更新记录一下。

而我前文提到的方法,在数据不多时我们可以采用,但数据较多的情况下,还是建议用下面的方法。

1.重置data中的所有数据

Object.assign(this.$data, this.$options.data())
  • this.$data:是现阶段的data数据,就是改变后的data数据
  • this.$options.data():是原始的data数据,就是页面刚加载时的data
  • data中若使用了this来访问props或methods,在重置data时,注意this.data时,注意this.options.data()的this指向,最好使用this.$options.data.call(this)。

2.重置data中的一个数据

Object.assign(this.formData, this.$options.data().formData)
​
this.formData = this.$options.data().formData

当我们采用以上方法重置表单数据时,我们可以采用下面方法重置表单校验:

this.$refs.form.clearValidate()