深入掌握ant-design的form异步校验(一)

一、前言
本文基于开源项目:

https://github1s.com/ant-design/ant-design

https://github.com/yiminghe/async-validator

很多小伙伴对antd的form表单校验不是很清楚,有些时候写了一些校验规则然后不是自己想要实现的效果、或者不清楚校验规则是什么无从写起。

下面我们一起来学习学习form的校验~

二、validateFields
 广东靓仔从文档找了个demo,代码如下:

<Form form={form} initialValues={{ aaa: '2' }}>
  <Form.Item name="aaa">
    <Input
      onChange={async () => {
        await sleep(0);
        try {
          await form.validateFields();
        } catch (e) {
        // do nothing
      }
     }}
    />
  </Form.Item>
<Form>
            
我们来看看validateFields的返回示例:

validateFields()
  .then(values => {
    /*
  values:
    {
      username: 'username',
      password: 'password',
    }
  */
  })
  .catch(errorInfo => {
    /*
    errorInfo:
      {
        values: {
          username: 'username',
          password: 'password',
        },
        errorFields: [
          { name: ['password'], errors: ['Please input your Password!'] },
        ],
        outOfDate: false,
      }
    */
  });
            
AntDesign的表单校验是基于async-validator,是支持异步校验的。  

三、async-validator介绍
官方介绍:Validate form asynchronous.
简单理解:async-validator是一个表单的异步验证的第三方库。
安装
npm i async-validator
使用
它基本用法包括定义描述符,将它分配给模式并将要验证的对象和回调函数传递给validate模式的方法:

import Schema from 'async-validator';
const descriptor = {
  name: {
    type: 'string',
    required: true,
    validator: (rule, value) => value === 'muji',
  },
  age: {
    type: 'number',
    asyncValidator: (rule, value) => {
      return new Promise((resolve, reject) => {
        if (value < 18) {
          // reject错误信息
          reject('too young');
        } else {
          resolve();
        }
      });
    },
  },
};
const validator = new Schema(descriptor);
validator.validate({ name: 'muji' }, (errors, fields) => {
  if (errors) {
    // 验证失败,errors 是一个包含所有错误的数组
    // fields 是一个以字段名称为键的对象
    // errors per field
    return handleErrors(errors, fields);
  }
  // 这里则是验证通过
});

// PROMISE USAGE
validator.validate({ name: 'muji', age: 16 }).then(() => {
  // 验证通过或没有错误消息
}).catch(({ errors, fields }) => {
  return handleErrors(errors, fields);
});
简单梳理下:传入验证规则对象,可以新建一个验证器对象。验证器对象的validate方法用于验证数据是否符合验证规则。

Validate有三个参数:

source:要验证的对象(必需)。

options:描述验证处理选项的对象(可选)。

callback:验证完成时调用的回调函数(可选)。

可以看出Validate返回了一个Promise 对象

rules规则
function(rule, value, callback, source, options)
rule:源描述符中的验证规则,对应于正在验证的字段名称。它总是被分配一个field带有被验证字段名称的属性。

value:正在验证的源对象属性的值。

callback:验证完成后调用的回调函数。它期望传递一个Error实例数组来指示验证失败。如果检查是同步的,可以直接返回一个falseorError或Error Array。

source:传递给validate方法的源对象。

options:其他选项。

options.messages:包含验证错误消息的对象,将与 defaultMessages 深度合并。

传递给validate或asyncValidate传递给验证函数的选项,以便在验证函数中引用瞬态数据(例如模型引用)。但是,保留了一些选项名称;如果使用选项对象的这些属性,它们将被覆盖。保留的属性messages是exception和error。



我们经常写rules为对象数组,针对单个字段的多个验证规则进很有用:

const descriptor = {
  email: [
    { type: 'string', required: true, pattern: Schema.pattern.email },
    {
      validator(rule, value, callback, source, options) {
        const errors = [];
        // 测试邮箱地址是否已经存在于数据库中
        // 如果确实则将验证错误添加到错误数组
        return errors;
      },
    },
  ],
};
type类型
可以使用的类型如下:

string: 必须是类型string。This is the default type.

number: 必须是类型number。

boolean: 必须是类型boolean。

method: 必须是类型function。

regexp: 必须是RegExp创建新的时不产生异常的实例或字符串RegExp。

integer: 必须是类型number和整数。

float: 必须是类型number和浮点数。

array: 必须是由 确定的数组Array.isArray。

object: 必须是 typeobject而不是Array.isArray。

enum: 值必须存在于enum.

date:值必须是有效的,由Date

url: 必须是类型url。

hex: 必须是类型hex。

email: 必须是类型email。

any: 可以是任何类型。

asyncValidator异步验证器
我们可以自定义指定字段的异步验证功能,代码如下:

const fields = {
  asyncField: {
    asyncValidator(rule, value, callback) {
      ajax({
        url: 'xx',
        value: value,
      }).then(function(data) {
        callback();
      }, function(error) {
        callback(new Error(error));
      });
    },
  },

  promiseField: {
    asyncValidator(rule, value) {
      return ajax({
        url: 'xx',
        value: value,
      });
    },
  },
};
validator验证器
我们可以为指定字段自定义验证功能,代码如下:
const fields = {
  field: {
    validator(rule, value, callback) {
      return value === 'test';
    },
    message: 'Value is not equal to "test".',
  },

  field2: {
    validator(rule, value, callback) {
      return new Error(`${value} is not equal to 'test'.`);
    },
  },
 
  arrField: {
    validator(rule, value) {
      return [
        new Error('Message 1'),
        new Error('Message 2'),
      ];
    },
  },
};

四、总结
    在我们阅读完官方文档后,我们一定会进行更深层次的学习,比如看下框架底层是如何运行的,以及源码的阅读。
    这里广东靓仔给下一些小建议:
在看源码前,我们先去官方文档复习下框架设计理念、源码分层设计
阅读下框架官方开发人员写的相关文章
借助框架的调用栈来进行源码的阅读,通过这个执行流程,我们就完整的对源码进行了一个初步的了解
接下来再对源码执行过程中涉及的所有函数逻辑梳理一遍

作者:广东靓仔


欢迎关注:前端早茶