对于elementUI form组件的表单验证器扩展,在校验一组内容时,校验完毕后一次返回校验的最终结果,且支持返回Promise

359 阅读4分钟

最近正在做一个vue的项目,使用到他的表单校验器时发现一个很古怪的问题,就是对部分字段校验的方法validateField的使用难以理解他的设计思路,咱们先看看官网怎么说:

image.png 就是说它这个方法能校验一个或者多个字段,但是我实际使用发现,它在校验一组字段时,是对传入的props遍历逐个调用这个callback函数,这个callback有一个校验失败的参数返回通过形参给到我们,举例子如果我传入一组字段为['name', 'age', 'six']这三个,当name通过校验了这个callback会被调用,这个errorMessage参数将什么都没有,如果age未通过,则这个errorMessag返回了校验规则中的错误提示文本.

这种逐个调用就导致我没法直接获得这一组数据到底有没有完整校验结束最终这一组字段的校验结果是什么需要我再去通过这个回调来自己统计。。。这个设计真奇怪,以至于我的代码变成了这样子: image.png 这让我觉得代码变了,变得不那么美了,也操作起来麻烦了,还要我自己定义一个变量来计数,太可怕了,当然我们可以优化一下让代码看起来少一点,像这样: image.png 问题也就在这,不管怎么优化我们都不得不自己去统计我校验的这一组内容是否通过,我可以说下我为什么纠结这个地方:我遇到这么一个场景,一个表格,用户可以点击表格末尾的按钮来编辑该行的内容,编辑完后,点击该行后面的保存按钮来保存被编辑的这一行内容,所以就需要再点击完成时,知道这一行的内容是否都通过了校验,以决定按钮是否能点下。至此虽然这个问题可以很好的解决,但是还是不美,那么我们就来改造它。

这种场景挺多的,写一个utils也是可以解决的,但是还是用着麻烦,那么考虑将他直接扩展到表单的原型上呢,用起来就方便多了,就可以像用validate一样了。

先来想想,既然我们要校验一组数据,如果都校验通过了那我们应该返回一个通过的状态,明确告诉调用者,全部通过了,如果有某个没通过,是不是应该把没通过的prop告诉调用者,这些prop值没通过校验。且还能支持让调用者一行代码就搞定那种

实现思路和上面的截图一样,只是我们将他做了更好的封装,能传入一组内容让其校验,但是要把最终都校验完成的结果给到调用者,定义一个计数变量count用来统计当前校验到第几个了,再定义一个数组failedFieldS来存放校验未通过的字段,最终通过Promise在所有的回调都执行完了,将我们统计的信息返回给调用者,如果failedFieldS为空,则说明并没有未通过的字段,此时我们resolve(true)即可,如果有未通过的字段,就调用reject(failedFieldS)爆出异常并能让调用者获得那些内容没通过校验。

最终代码实现如下:

/**
 * 设置表单指定filed得内容
 * @param { path[] | undefined } fieldPaths - 要是设置得字段path: value对象或者path集合
 * @return { Promise } Promise -如果校验全部通过则resolve(true),否则返回未通过的form prop path
 * @description
 * 接收一组form prop path值,如果传入的是空数组或者undefined,则对整个表单校验
 */
export function validateFields(fieldPaths) {
  const fieldsType = Object.prototype.toString.call(fieldPaths);
  if (!['[object Array]', '[object Undefined]', '[object Undefined]'].includes(fieldsType)) {
    throw new Error(`validateFields expecting to get an '[object Array]', but got ${fieldsType}`)
  }

  const _this = this;
  let _fieldPaths = fieldPaths

  if (fieldsType === '[object Undefined]' || (fieldsType == '[object Array]' && _fieldPaths.length === 0)) {
    _fieldPaths = _this.fields.map(({prop}) => prop)
  };

  let count = 0;
  const failedFieldS = [];

  return new Promise((resolve, reject) => {
    _this.validateField(_fieldPaths, (e) => {
      if (e) {
        failedFieldS.push(_fieldPaths[count])
      }
      count++;
    });

    if (failedFieldS.length === 0) {
      resolve(true)
    } else {
      reject(failedFieldS)
    }
  })
}

然后将它挂载到Form的methods对象上:

main.js

import ElementUI from 'element-ui';
import { validateFields } from './elFormMethodExtend.js';

ElementUI.Form.methods.setFields = setFields;
ElementUI.Form.methods.validateFields = validateFields

调用者使用:

<el-button @click="checkForm">校验整个表单</el-button>

// 可通过`try catch`捕获抛出来的异常信息,可以明确知道那个没通过。是不是就很方便了呢。
async checkForm() {
  try {
    const form = this.$refs.form
    const is = await form.validateFields(['name', 'age', 'six']);
    console.log(is) // true
  } catch (error) {
    console.log(error) // ['未校验通过的字段string']
  }
}

当然你如果不关注异常信息,只关注最终的成功与否,那么我们需要写的代码将更加简单:

await ruleForm.validateFields(['name', 'age', 'six']);
// 如果报出了异常,那么await 之下的代码是不会执行的。
// TODO