element组件dialog中嵌套form表单校验提示混乱的解决方案

2,276 阅读2分钟

一、起因:

在一个dialog中提交一个form表单或者编辑一个form表单,表单校验的出现时机总是感觉不对,问题反复地出现,很折磨人。

二、场景:

新增/编辑功能共用一个表单

三、问题描述:

  • 操作一:

点击新增按钮--显示dialog--填写表单--校验通过--点击确定按钮--保存成功

点击新增按钮--显示dialog--显示校验提示...

  • 操作二:

点击编辑按钮--显示dialog--回显表单--编辑--校验通过--点击确认按钮--保存成功

点击新增按钮--显示dialog--显示校验提示...

在最后一步的新增操作,我什么都没填的情况下,不应该显示校验提示,那为啥会出现校验提示,这和我们的业务逻辑有关系。

在点击新增按钮,显示的表单,必须是一个空表单,为了实现这一步,我们一般的操作有以下两种:

  • 在上一次dialog关闭之后,把表单置空
  • 在本次dialog显示之前把表单置空

上面的实现思路是没有问题的,其中的处理也是必不可少,不可避免的,因为共用了一个表单来实现新增,编辑功能。

问题来了:

dialog打开之后,form注册好之后,form-item的rule也就注册好了,至于rule的触发就看你是否满足触发条件,一般会有这两种情况:

  • 满足预设的触发条件,比如:change,blur
  • 调用函数去触发,比如:
  this.$refs.form.validate((valid) => {
    if (valid) {}
  })

当我们操作“点击新增按钮--显示dialog--显示校验提示...”,由于置空了表单,就触发了change事件,于是触发了校验,发现并不满足条件,因此显示校验提示。

四、解决方案

针对这个问题,需要从两个方面去解决:

  • 手动置空表单

    监听dialog关闭行为,利用组件提供的回调函数close() ,在函数里面手动置空表单, 比如:

  close() {
    this.form = {
      custId: '',
      prodCode: '',
      serviceId: '',
      openState: ''
    };
  }

这里不建议使用el-form提供的方法resetFields()来对整个表单进行重置,将所有字段值重置为初始值并移除校验结果, 因为在各种情景下表单的初始值会被修改,后面调用 resetFields() 则不会生效(不会是定义时的空值),具体的场景要具体的分析。并不是手动置空就很丢人,如果嫌啰嗦,可以使用:

  for(let key in this.form){
    this.$set(this.form, key, '');
  }
  • 在合适的时机清空校验结果

    这里合适的时机就是监听dialog的打开行为,利用组件提供的回调函数open(), 在函数里面手动调用表单提供的清除校验的方法,比如:

  open() {
    this.$nextTick(() => {
      if (this.$refs.form) {
        this.$refs.form.clearValidate();
      }
    });
  },

这里为什么要在nextTick中去清除表单的校验结果呢? 那是因为在第一次渲染页面时,dialog注册,但是el-form并没有注册,只有dialog在打开之后才会被注册,但是并不意味着dialog一打开el-form就注册好了,也就说, 不管是调用dialog的show()方法还是open()方法都不能马上获取form节点,因此以下判断是没有满足的

  if (this.$refs.form) {
    this.$refs.form.clearValidate();
  }

为了保证dialog在打开之后可以获取到form节点,从而调用清除校验方法,需要将清除校验操作放在nextTick中。

基于表单放在dialog中的这种场景,需要注意的是:

  • dialog在注册时周期函数会全部执行完,之后不管怎么样打开关闭dialog都不会在重复执行周期函数
  • 在dialog的周期函数中给表单赋值是可以的,但是避免这么做,一切与表单相关的操作建议在确保el-form组件注册完成之后进行,这样可以保持dialog和el-form的相对独立