一、起因:
在一个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的相对独立