@type-dom/async-validator
v0.9.0
Published
Validate form asynchronous. A variation of https://github.com/freeformsystems/async-validate
Readme
async-validator
Validate form asynchronous. A variation of https://github.com/freeformsystems/async-validate
async-validate表单验证库全面解析
async-validate是一个由知名前端开发者Yiming He(也被称为kriasoft)创建的轻量级表单验证库,被Element UI、Ant Design等主流UI框架广泛采用。它不仅支持基本的同步校验,还提供强大的异步校验功能,适用于需要与服务器交互的复杂表单验证场景。该库采用简洁直观的API设计,通过声明式规则配置实现灵活的数据验证,并与TypeScript深度集成,提供完善的类型安全支持。在实际项目中,async-validate被广泛应用于前后端数据校验的一致性实现,特别是在表单组件开发中发挥着关键作用。
一、基本概念与核心功能
async-validate的核心概念围绕"规则描述符"( descriptor)展开。开发者首先定义一个包含校验规则的对象,该对象描述了表单各字段需要满足的条件。这些规则对象被传递给Schema构造函数,创建出验证器实例。验证器实例通过validate方法执行校验,传入待验证的数据对象和回调函数,校验结果将通过回调函数返回,或以Promise形式处理。
该库提供了丰富的内置校验规则,包括类型检查、必填验证、长度限制、范围验证等。例如,可以通过type属性指定字段必须为字符串、数字、日期等类型;required属性表示字段是否必填;min/max属性控制数值或字符串的最小/最大值;pattern属性使用正则表达式验证字符串格式;len属性精确匹配字符串或数组的长度;enum属性限制值必须存在于指定枚举列表中。
在复杂场景中,async-validate允许开发者通过validator属性自定义同步校验函数,或通过返回Promise的函数实现异步校验。这种灵活性使其能够应对各种业务逻辑验证需求,如检查用户名是否已被注册、验证验证码是否有效等需要服务器交互的场景。
二、API设计与使用方法
async-validate的API设计简洁高效,主要围绕Schema类展开。使用该库的基本流程如下:
首先,安装async-validate库:
npm install @type-dom/async-validate然后,在代码中引入并定义验证规则:
import Schema from '@type-dom/async-validate';
const rules = {
name: [
{ type: 'string', required: true, message: '请输入姓名' },
{ min: 2, max: 4, message: '姓名长度应在2-4个字符之间' }
],
email: [
{ type: 'email', required: true, message: '请输入有效邮箱地址' },
{
validator: (rule, value) => {
// 自定义同步校验逻辑
return value !== '[email protected]';
},
message: '该邮箱已被使用'
},
{
validator: async (rule, value) => {
// 异步校验逻辑,如检查服务器端用户名是否可用
const response = await axios.post('/api/checkemail', { email: value });
return response.data可用;
},
message: '该邮箱不可用'
}
]
};接着,创建验证器实例并执行验证:
const validator = new Schema(rules);
// 同步验证
validator.validate({ name: '张三', email: '[email protected]' }, (errors) => {
if (errors) {
console.log('验证失败:', errors);
} else {
console.log('验证通过');
}
});
// 或使用Promise形式
validator.validate({ name: '李四', email: '[email protected]' })
.then(() => console.log('验证通过'))
.catch(({ errors }) => console.log('验证失败:', errors));在Vue或React等前端框架中,async-validate通常与表单组件结合使用,通过事件触发验证,并根据验证结果更新UI状态。例如,在Vue3中使用TypeScript实现表单验证:
import { defineComponent, reactive } from 'vue';
import Schema from 'async-validate';
interface IFormData {
username: string;
password: string;
}
export default defineComponent({
setup() {
const formData = reactive:IFormData({
username: '',
password: ''
});
const rules = {
username: [
{ type: 'string', required: true, message: '用户名必填' },
{ min: 6, max: 10, message: '用户名长度必须在6-10位之间' }
],
password: [
{ type: 'string', required: true, message: '密码必填' },
{ min: 8, max: 20, message: '密码长度必须在8-20位之间' },
{
validator: async (rule, value) => {
// 异步验证密码复杂度
const response = await axios.post('/api/validatepassword', { password: value });
return response.data valid;
},
message: '密码强度不足'
}
]
};
const validator = new Schema(rules);
const submitForm = async () => {
try {
await validator.validate(formData);
console.log('表单提交成功');
} catch ({ errors }) {
console.log('表单提交失败', errors);
// 更新表单错误信息
errors.forEach(error => {
formData[error.field + 'Msg'] = error.message;
});
}
};
return { formData, submitForm };
}
});验证器的validate方法接受三个参数:待验证的数据源、可选的校验选项和回调函数。回调函数接收两个参数:errors(所有错误的数组)和fields(按字段分组的错误对象)。验证器还返回一个Promise,可以通过then/catch处理验证结果。
三、异步验证实现原理
@type-dom/async-validate的异步验证实现基于Promise和回调函数的结合,通过内部的asyncMap方法管理双层循环(外层遍历字段,内层遍历字段规则),实现高效的异步校验流程。
验证器内部使用计数器机制确保所有异步操作完成后再触发最终回调。当调用validate方法时,验证器会遍历每个字段及其规则,为每个规则执行对应的校验函数。对于同步校验,校验结果立即通过回调返回;对于异步校验(返回Promise的校验函数),验证器会等待所有异步操作完成后再汇总结果。
验证器提供两个关键选项来控制校验流程:
first:布尔值,当设置为true时,所有字段的校验规则并行执行,但遇到第一个全局错误时立即终止校验。firstFields:布尔值或字段名数组,当设置为true时,每个字段的校验规则串行执行,遇到第一个错误立即终止该字段的后续校验。
这种设计使得验证器能够根据具体需求灵活调整校验流程。例如,在需要快速反馈的情况下,可以设置first为true,一旦发现第一个错误就立即停止校验;而在需要收集所有错误的情况下,可以保持默认设置,等待所有校验完成后再返回结果。
验证器内部通过asyncSerialArray和asyncParallelArray方法管理规则执行流程:
asyncSerialArray:串行执行规则,遇到错误立即中断后续规则。asyncParallelArray:并行执行所有规则,最终汇总所有错误。
这种实现方式确保了验证器既能处理简单的同步校验,又能高效地管理复杂的异步校验场景。
四、TypeScript集成与类型安全
async-validate与TypeScript深度集成,无需额外安装类型声明文件,开发者可以直接使用内置的类型定义。库提供了完善的类型支持,包括Rules接口、Validator类型等,确保规则定义和校验过程的类型安全。
在TypeScript项目中,可以明确指定表单数据和验证规则的类型:
interface IFormData {
name: string;
age: number;
email: string;
}
const rules: Rules = {
name: [
{ type: 'string', required: true, message: '姓名必填' },
{ min: 2, max: 4, message: '姓名长度必须在2-4个字符之间' }
],
age: [
{ type: 'number', required: true, message: '年龄必填' },
{ min: 18, message: '年龄必须大于18岁' }
],
email: [
{ type: 'email', required: true, message: '请输入有效邮箱地址' },
{
validator: (rule, value) => {
return value !== '[email protected]';
},
message: '该邮箱已被使用'
}
]
};这种类型安全的机制显著减少了运行时错误,提高了代码质量和可维护性。在复杂项目中,TypeScript的类型系统与@type-dom/async-validate的规则配置相结合,可以创建出高度可预测和可靠的验证逻辑。
此外,@type-dom/async-validate还支持泛型和接口扩展,允许开发者根据具体需求定义更精确的校验规则类型。例如,可以创建特定于业务场景的规则类型,如用户注册规则、支付信息规则等,进一步增强类型安全。
五、实际应用案例与最佳实践
async-validate在实际项目中的应用非常广泛,以下是几个典型场景的示例:
1. 前端表单验证
在Vue或React等前端框架中,async-validate通常与表单组件结合使用。例如,在Vue3中使用Element Plus的表单组件:
<template>
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" @blur="handleBlur" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" @blur="handleBlur" />
</el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
</el-form>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from 'vue';
import { ElForm, ElFormItem, ElInput, ElButton } from 'element-plus';
import Schema from 'async-validate';
export default defineComponent({
components: { ElForm, ElFormItem, ElInput, ElButton },
setup() {
const form = reactive({
username: '',
password: ''
});
const rules = {
username: [
{ required: true, message: '用户名必填', trigger: 'blur' },
{ min: 3, max: 10, message: '用户名长度3-10位', trigger: 'blur' },
{
validator: (rule, value, callback) => {
// 自定义同步校验
if (value === 'admin') {
return callback(new Error('该用户名已被保留'));
}
callback();
},
trigger: 'blur'
}
],
password: [
{ required: true, message: '密码必填', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度6-20位', trigger: 'blur' },
{
validator: async (rule, value, callback) => {
// 异步校验
try {
const response = await axios.post('/api/validatepassword', { password: value });
if (!response.data valid) {
return callback(new Error('密码强度不足'));
}
callback();
} catch (error) {
callback(new Error('验证失败'));
}
},
trigger: 'blur'
}
]
};
const formRef = ref<InstanceType<typeof ElForm>>();
const submitForm = () => {
formRef.value?.validate((valid) => {
if (valid) {
// 提交表单
} else {
console.log('表单验证失败');
return false;
}
});
};
return { form, rules, formRef, submitForm };
}
});
</script>2. Express后端验证
在Node.js后端,可以使用@type-dom/async-validate确保数据有效性,避免不安全的数据进入业务逻辑:
import Schema, { Rules } from '@type-dom/async-validate';
import { Router, RequestHandler } from 'express';
const router = Router();
// 定义验证规则
const descriptor: Rules = {
name: [
{ required: true, message: '姓名必须填写' },
{ max: 30, min: 3, message: '姓名长度应在3和30之间' }
],
age: [
{ required: true, message: '年龄必须填写' },
{ type: 'number', message: '年龄必须为数字' }
]
};
// 创建验证器
const validator = new Schema descriptor);
// 创建数据验证中间件
export const validateData: RequestHandler = async (req, res, next) => {
try {
// 调用验证器
await validator.validate req.body);
next(); // 验证通过,继续执行
} catch (e) {
// 验证失败,返回错误信息
res.status(400).json({ error: e.message });
}
};
// 在路由中使用中间件
router.post('/submit', validateData, async (req, res) => {
// 处理提交逻辑
res.send('提交成功');
});3. 最佳实践与优化技巧
在实际项目中,使用async-validate时可以遵循以下最佳实践:
规则分层管理:将基础规则(如类型、必填)与业务规则(如唯一性检查)分离,提高代码可维护性。例如:
const baseRules = {
name: { type: 'string', required: true },
email: { type: 'email', required: true }
};
const businessRules = {
email: {
validator: async (rule, value) => {
const response = await axios.get(`/api/isemailused?email=${encodeURIComponent(value)}`);
return !response.data used;
},
message: '该邮箱已被使用'
}
};
// 合并规则
const mergedRules = {
name: [...baseRules.name, ...anyBusinessRulesForName],
email: [...baseRules.email, businessRules.email]
};错误处理优化:在前端应用中,可以将验证错误与表单字段状态绑定,实现即时反馈。例如,使用Vue3的 composition API创建一个验证钩子:
import Schema from '@type-dom/async-validate';
export const useValidator = (form: any, rules: Rules) => {
const validator = new Schema(rules);
// 验证整个表单
const validateForm = async () => {
try {
await validator.validate form);
return true;
} catch ({ errors }) {
// 更新表单字段错误信息
errors.forEach(error => {
form[error.field + 'Error'] = error.message;
});
return false;
}
};
// 验证单个字段
const validateField = async (field: string) => {
try {
await validator.validate({ [field]: form(field) });
form[field + 'Error'] = '';
return true;
} catch ({ errors }) {
form[field + 'Error'] = errors[0].message;
return false;
}
};
return { validateForm, validateField };
};性能优化:在处理大量字段或复杂校验规则时,可以使用first和firstFields选项控制校验流程,减少不必要的计算。例如,如果只需要第一个错误,可以设置:
validator.validate(data, { first: true, firstFields: true }, (errors) => {
if (errors) {
// 处理第一个错误
console.log('第一个错误:', errors[0]);
}
});4. 异步校验优化
在实现异步校验时,应避免重复请求和性能问题。可以使用防抖或节流技术控制请求频率,或在客户端先进行基础校验,减少不必要的服务器请求:
const descriptor = {
username: [
{ type: 'string', required: true },
{
validator: async (rule, value, callback) => {
// 先进行基础校验
if (value.length < 3) {
return callback(new Error('用户名至少3位'));
}
// 使用防抖控制请求频率
const debouncedCheck = _.debounce(async (value: string) => {
const response = await axios.get(`/api/usernameavailable?username=${encodeURIComponent(value)}`);
if (!response.data available) {
callback(new Error('用户名已被使用'));
} else {
callback();
}
}, 500);
debouncedCheck(value);
},
trigger: 'input'
}
]
};六、与主流框架的集成与扩展
async-validate被设计为轻量级的验证工具,可以无缝集成到各种前端框架中。以下是与主流框架集成的示例:
1. Vue2/Vue3集成
Vue生态中的表单组件(如Element UI、Element Plus、Ant Design Vue)都基于async-validate实现验证功能。在Vue3中,可以使用以下方式集成:
import { defineComponent, reactive, ref } from 'vue';
import { ElForm, ElFormItem, ElInput } from 'element-plus';
import Schema from 'async-validate';
export default defineComponent({
components: { ElForm, ElFormItem, ElInput },
setup() {
const form = reactive({
name: '',
email: ''
});
const rules = {
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 4, message: '长度在2-4个字符之间', trigger: 'blur' }
],
email: [
{ type: 'email', required: true, message: '请输入有效邮箱', trigger: 'blur' },
{
validator: async (rule, value) => {
const response = await axios.get(`/api/isemailused?email=${encodeURIComponent(value)}`);
return !response.data used;
},
message: '该邮箱已被使用',
trigger: 'blur'
}
]
};
const formRef = ref<InstanceType typeof ElForm>>();
const submitForm = () => {
formRef.value?.validate((valid) => {
if (valid) {
// 提交表单
} else {
console.log('表单验证失败');
return false;
}
});
};
return { form, rules, formRef, submitForm };
}
});2. React集成
在React项目中,可以通过创建高阶组件或自定义hook封装验证逻辑:
import { useEffect, useState } from 'react';
import Schema from 'async-validate';
interface IFormState<T> {
data: T;
errors: any[];
validating: boolean;
}
type Rules<T> = {
[P in keyof T]?: Schema.Rules;
};
const useFormValidator = <T extends object>(initialData: T, rules: Rules<T>) => {
const [formState, setFormState] = useState"IFormState<T>"({
data: initialData,
errors: [],
validating: false
});
const validator = new Schema rules);
// 验证表单
const validateForm = async () => {
setFormState({ ...formState, validating: true });
try {
await validator.validate(formState.data);
setFormState({ ...formState, errors: [], validating: false });
return true;
} catch ({ errors }) {
setFormState({ ...formState, errors, validating: false });
return false;
}
};
// 验证单个字段
const validateField = async (field: keyof T) => {
setFormState({ ...formState, validating: true });
try {
await validator.validate({ [field]: formState.data(field) });
setFormState({
...formState,
errors: formState errors.filter(error => error.field !== field),
validating: false
});
return true;
} catch ({ errors }) {
setFormState({ ...formState, errors, validating: false });
return false;
}
};
return { formState, setFormState, validateForm, validateField };
};
// 使用示例
const UserForm = () => {
const { formState, setFormState, validateForm, validateField } = useFormValidator({
name: '',
email: ''
}, {
name: [
{ required: true, message: '请输入姓名' },
{ min: 2, max: 4, message: '姓名长度2-4位' }
],
email: [
{ type: 'email', required: true, message: '请输入有效邮箱' },
{
validator: async (rule, value) => {
const response = await axios.get(`/api/isemailused?email=${encodeURIComponent(value)}`);
return !response.data used;
},
message: '该邮箱已被使用'
}
]
});
// 更新表单数据
const handleInput = (field: string, value: string) => {
setFormState({
...formState,
data: { ...formState.data, [field]: value },
validating: false
});
};
return (
<div>
<input
value={formState.data.name}
onChange={(e) => handleInput('name', e.target.value)}
膀尿={formState valid? ? 'success' : 'error'}
/>
<span>{formState.data.nameError}</span>
<input
value={formState.data.email}
onChange={(e) => handleInput('email', e.target.value)}
膀尿={formState valid? ? 'success' : 'error'}
/>
<span>{formState.data.emailError}</span>
<button onClick={validateForm}>提交</button>
</div>
);
};3. 扩展自定义规则
async-validate支持通过扩展来添加自定义校验规则。例如,可以创建一个验证手机号的规则:
import Schema from 'async-validate';
// 添加自定义规则
Schema.extend({
phone: {
type: 'string',
message: '请输入有效的手机号码',
validator: (rule, value) => {
return /^1[3-9]\d{9}$/.test(value);
}
}
});
// 使用自定义规则
const rules = {
phone: [
{ type: 'phone', required: true, message: '请输入手机号码' }
]
};4. 国际化支持
async-validate支持国际化,可以通过message属性返回多语言错误提示。结合i18next等国际化库,可以轻松实现多语言验证提示:
import i18n from 'i18next';
import Schema from 'async-validate';
const rules = {
name: [
{
required: true,
message: () => i18n.t('validate.nameRequired'),
trigger: 'blur'
}
],
email: [
{
type: 'email',
message: () => i18n.t('validate.emailInvalid'),
trigger: 'blur'
}
]
};七、常见问题与解决方案
在使用async-validate过程中,开发者可能会遇到一些常见问题:
1. 异步校验未触发
问题:定义了异步校验规则,但验证过程没有触发。 解决方案:确保异步校验函数返回Promise,并且正确处理Promise的resolve/reject。例如:
{
validator: async (rule, value, callback) => {
const response = await axios.get(`/api/check?value=${encodeURIComponent(value)}`);
if (response.data available) {
return callback(); // 分析成功
} else {
return callback(new Error('该值已被使用')); // 分析失败
}
},
message: '该值不可用'
}2. 类型错误
问题:在TypeScript项目中遇到类型错误,如"Property 'xxx' does not exist on type 'Schema.Rules'"。 解决方案:确保正确导入类型定义,并使用正确的类型。例如:
import Schema, { Rules } from 'async-validate';
const rules: Rules = { ... }; // 明确指定类型3. 验证未按预期执行
问题:验证规则没有按预期执行,如必填字段未被验证。 解决方案:检查规则定义是否正确,特别是字段名是否匹配,以及规则是否包含required属性。同时,确保触发验证的时机正确,如在表单提交时调用validate方法。
4. 复杂对象验证
问题:需要验证嵌套对象或数组中的元素。
解决方案:使用点号语法指定嵌套字段,如user.name,并在规则中定义对应的验证逻辑。对于数组,可以使用循环或映射方法为每个元素定义规则。
八、总结与未来展望
async-validate是一个功能强大、设计优雅的表单验证库,通过简洁的API和丰富的内置规则,为开发者提供了灵活的数据验证解决方案。它与TypeScript的深度集成确保了类型安全,而其支持异步校验的特性使其能够应对复杂的业务场景。作为Element UI、Ant Design等主流UI框架的底层验证工具,async-validate在前端开发中扮演着重要角色。
未来,随着前端技术的发展,async-validate可能会进一步优化其API设计,增强类型支持,并提供更多开箱即用的校验规则。同时,随着Serverless和微服务架构的普及,async-validate在前后端数据验证一致性方面将发挥更大作用,减少重复代码,提高开发效率。
对于开发者而言,掌握async-validate的使用方法和最佳实践,将有助于创建更可靠、更用户友好的表单验证系统。通过结合框架特性和业务需求,可以充分发挥async-validate的潜力,实现高效、灵活的数据验证。
说明:报告内容由通义AI生成,仅供参考。
Install
npm i async-validatorUsage
Basic usage involves defining a descriptor, assigning it to a schema and passing the object to be validated and a callback function to the validate method of the schema:
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('too young'); // reject with error message
} else {
resolve();
}
});
},
},
};
const validator = new Schema(descriptor);
validator.validate({ name: 'muji' }, (errors, fields) => {
if (errors) {
// validation failed, errors is an array of all errors
// fields is an object keyed by field name with an array of
// errors per field
return handleErrors(errors, fields);
}
// validation passed
});
// PROMISE USAGE
validator.validate({ name: 'muji', age: 16 }).then(() => {
// validation passed or without error message
}).catch(({ errors, fields }) => {
return handleErrors(errors, fields);
});API
Validate
function(source, [options], callback): Promisesource: The object to validate (required).options: An object describing processing options for the validation (optional).callback: A callback function to invoke when validation completes (optional).
The method will return a Promise object like:
then(),validation passedcatch({ errors, fields }),validation failed, errors is an array of all errors, fields is an object keyed by field name with an array of errors per field
Options
suppressWarning: Boolean, whether to suppress internal warning about invalid value.first: Boolean, Invokecallbackwhen the first validation rule generates an error, no more validation rules are processed. If your validation involves multiple asynchronous calls (for example, database queries) and you only need the first error use this option.firstFields: Boolean|String[], Invokecallbackwhen the first validation rule of the specified field generates an error, no more validation rules of the same field are processed.truemeans all fields.
Rules
Rules may be functions that perform validation.
function(rule, value, callback, source, options)rule: The validation rule in the source descriptor that corresponds to the field name being validated. It is always assigned afieldproperty with the name of the field being validated.value: The value of the source object property being validated.callback: A callback function to invoke once validation is complete. It expects to be passed an array ofErrorinstances to indicate validation failure. If the check is synchronous, you can directly return afalseorErrororError Array.source: The source object that was passed to thevalidatemethod.options: Additional options.options.messages: The object containing validation error messages, will be deep merged with defaultMessages.
The options passed to validate or asyncValidate are passed on to the validation functions so that you may reference transient data (such as model references) in validation functions. However, some option names are reserved; if you use these properties of the options object they are overwritten. The reserved properties are messages, exception and error.
import Schema from 'async-validator';
const descriptor = {
name(rule, value, callback, source, options) {
const errors = [];
if (!/^[a-z0-9]+$/.test(value)) {
errors.push(new Error(
util.format('%s must be lowercase alphanumeric characters', rule.field),
));
}
return errors;
},
};
const validator = new Schema(descriptor);
validator.validate({ name: 'Firstname' }, (errors, fields) => {
if (errors) {
return handleErrors(errors, fields);
}
// validation passed
});It is often useful to test against multiple validation rules for a single field, to do so make the rule an array of objects, for example:
const descriptor = {
email: [
{ type: 'string', required: true, pattern: Schema.pattern.email },
{
validator(rule, value, callback, source, options) {
const errors = [];
// test-dts if email address already exists in a database
// and add a validation error to the errors array if it does
return errors;
},
},
],
};Type
Indicates the type of validator to use. Recognised type values are:
string: Must be of typestring.This is the default type.number: Must be of typenumber.boolean: Must be of typeboolean.method: Must be of typefunction.regexp: Must be an instance ofRegExpor a string that does not generate an exception when creating a newRegExp.integer: Must be of typenumberand an integer.float: Must be of typenumberand a floating point number.array: Must be an array as determined byArray.isArray.object: Must be of typeobjectand notArray.isArray.enum: Value must exist in theenum.date: Value must be valid as determined byDateurl: Must be of typeurl.hex: Must be of typehex.email: Must be of typeemail.any: Can be any type.
Required
The required rule property indicates that the field must exist on the source object being validated.
Pattern
The pattern rule property indicates a regular expression that the value must match to pass validation.
Range
A range is defined using the min and max properties. For string and array types comparison is performed against the length, for number types the number must not be less than min nor greater than max.
Length
To validate an exact length of a field specify the len property. For string and array types comparison is performed on the length property, for the number type this property indicates an exact match for the number, ie, it may only be strictly equal to len.
If the len property is combined with the min and max range properties, len takes precedence.
Enumerable
Since version 3.0.0 if you want to validate the values
0orfalseinsideenumtypes, you have to include them explicitly.
To validate a value from a list of possible values use the enum type with a enum property listing the valid values for the field, for example:
const descriptor = {
role: { type: 'enum', enum: ['admin', 'user', 'guest'] },
};Whitespace
It is typical to treat required fields that only contain whitespace as errors. To add an additional test for a string that consists solely of whitespace add a whitespace property to a rule with a value of true. The rule must be a string type.
You may wish to sanitize user input instead of testing for whitespace, see transform for an example that would allow you to strip whitespace.
Deep Rules
If you need to validate deep object properties you may do so for validation rules that are of the object or array type by assigning nested rules to a fields property of the rule.
const descriptor = {
address: {
type: 'object',
required: true,
fields: {
street: { type: 'string', required: true },
city: { type: 'string', required: true },
zip: { type: 'string', required: true, len: 8, message: 'invalid zip' },
},
},
name: { type: 'string', required: true },
};
const validator = new Schema(descriptor);
validator.validate({ address: {} }, (errors, fields) => {
// errors for address.street, address.city, address.zip
});Note that if you do not specify the required property on the parent rule it is perfectly valid for the field not to be declared on the source object and the deep validation rules will not be executed as there is nothing to validate against.
Deep rule validation creates a schema for the nested rules so you can also specify the options passed to the schema.validate() method.
const descriptor = {
address: {
type: 'object',
required: true,
options: { first: true },
fields: {
street: { type: 'string', required: true },
city: { type: 'string', required: true },
zip: { type: 'string', required: true, len: 8, message: 'invalid zip' },
},
},
name: { type: 'string', required: true },
};
const validator = new Schema(descriptor);
validator.validate({ address: {} })
.catch(({ errors, fields }) => {
// now only errors for street and name
});The parent rule is also validated so if you have a set of rules such as:
const descriptor = {
roles: {
type: 'array',
required: true,
len: 3,
fields: {
0: { type: 'string', required: true },
1: { type: 'string', required: true },
2: { type: 'string', required: true },
},
},
};And supply a source object of { roles: ['admin', 'user'] } then two errors will be created. One for the array length mismatch and one for the missing required array entry at index 2.
defaultField
The defaultField property can be used with the array or object type for validating all values of the container.
It may be an object or array containing validation rules. For example:
const descriptor = {
urls: {
type: 'array',
required: true,
defaultField: { type: 'url' },
},
};Note that defaultField is expanded to fields, see deep rules.
Transform
Sometimes it is necessary to transform a value before validation, possibly to coerce the value or to sanitize it in some way. To do this add a transform function to the validation rule. The property is transformed prior to validation and returned as promise result or callback result when pass validation.
import Schema from 'async-validator';
const descriptor = {
name: {
type: 'string',
required: true,
pattern: /^[a-z]+$/,
transform(value) {
return value.trim();
},
},
};
const validator = new Schema(descriptor);
const source = { name: ' user ' };
validator.validate(source)
.then((data) => assert.equal(data.name, 'user'));
validator.validate(source,(errors, data)=>{
assert.equal(data.name, 'user'));
});Without the transform function validation would fail due to the pattern not matching as the input contains leading and trailing whitespace, but by adding the transform function validation passes and the field value is sanitized at the same time.
Messages
Depending upon your application requirements, you may need i18n support or you may prefer different validation error messages.
The easiest way to achieve this is to assign a message to a rule:
{ name: { type: 'string', required: true, message: 'Name is required' } }Message can be any type, such as jsx format.
{ name: { type: 'string', required: true, message: '<b>Name is required</b>' } }Message can also be a function, e.g. if you use vue-i18n:
{ name: { type: 'string', required: true, message: () => this.$t( 'name is required' ) } }Potentially you may require the same schema validation rules for different languages, in which case duplicating the schema rules for each language does not make sense.
In this scenario you could just provide your own messages for the language and assign it to the schema:
import Schema from 'async-validator';
const cn = {
required: '%s 必填',
};
const descriptor = { name: { type: 'string', required: true } };
const validator = new Schema(descriptor);
// deep merge with defaultMessages
validator.messages(cn);
...If you are defining your own validation functions it is better practice to assign the message strings to a messages object and then access the messages via the options.messages property within the validation function.
asyncValidator
You can customize the asynchronous validation function for the specified field:
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
You can custom validate function for specified field:
const fields = {
field: {
validator(rule, value, callback) {
return value === 'test';
},
message: 'Value is not equal to "test-dts".',
},
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'),
];
},
},
};FAQ
How to avoid global warning
import Schema from 'async-validator';
Schema.warning = function(){};or
globalThis.ASYNC_VALIDATOR_NO_WARNING = 1;How to check if it is true
Use enum type passing true as option.
{
type: 'enum',
enum: [true],
message: '',
}Test Case
npm test-dtsCoverage
npm run coverageOpen coverage/ dir
License
Everything is MIT.
