一次表单验证不生效引发的思考

3,027 阅读2分钟

使用elementUI的表单验证,输入框本身不可编辑,需要点击选择按钮,选择完了之后,给表单赋值。如图:

选择成功之后,点击确定,表单验证不通过。我通常就是通过试错的方式去修复问题,即使改好了,也不知道为什么,但是这一次我决定一探究竟!

首先我找到了eleUI的form组件的源码,找到validate函数,关键代码:

this.fields.forEach(field => {
  field.validate('', (message, field) => {
    if (message) {
      valid = false;
    }
    invalidFields = objectAssign({}, invalidFields, field);
    if (typeof callback === 'function' && ++count === this.fields.length) {
      callback(valid, invalidFields);
    }
  });
});

fields是啥,我们使用DevTool看看

原来是el-form-item的实例,然后再打开form-item组件的源码,找到validate函数,其中使用到了async-validator这个库,这个库可以验证prop是否符合我们定义的规则,具体可以自行了解。另外还有一行关键代码model[this.prop] = this.fieldValue;,这说明prop的值用的是fieldValue的值,在组件里搜了一下fieldValue可以知道,fieldValue是一个计算属性,其中涉及到一个方法getPropByPath,这个我们后面在看,先在DevTool查看fieldValue的值,我们发现,fieldValue为undefined

并且我查看了外层表单绑定的字段中,是给上了值了的。于是我在fieldValue的计算属性的函数中打了断点。发现在赋值之后,记这个计算属性的方法根本就没有执行。这个方案代码:

fieldValue() {
  const model = this.form.model;
  if (!model || !this.prop) { return; }

  let path = this.prop;
  if (path.indexOf(':') !== -1) {
    path = path.replace(/:/, '.');
  }

  return getPropByPath(model, path, true).v;
}

很明显它的值是依赖this.form.model的,this.form.model就是form表单绑定的对象也就是this.form.model的值变了,但是没有触发计算属性函数的执行。来到这里,心里感觉还得去看vue的源码,看watcher之类的。。。有点放弃的冲动了,但还是先看看getPropByPath是啥吧......

getPropByPath是在util.js里,其实也只是拿到path对应的值而已。换一个思路,计算数据没有触发,有可能是这个动态prop是没有添加对应的watcher,所以我在chrome里给form-item的validate函数打了个断点(可以从控制台的warning进来)

再触发一下表单验证,然后切换到console,输入_this.form.model

果然是的!!!所以确定最终解决方案:给emergencyUuid这个prop赋值时使用$set设置

另外提一下为什么不初始化的时候就给emergencyUuid这个key赋一个初始值,因为这个表单可能是写成一个组件,初始值可能是在父组件中初始化的,那么多次引用子组件就需要每次都增加一个初始值了,所以在子组件内统一处理更合理