Skip to content

Commit

Permalink
feat(EasyValidator): 支持动态设置 message
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Jun 1, 2019
1 parent 78a752d commit 3827577
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 28 deletions.
27 changes: 22 additions & 5 deletions src/EasyValidator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Data = {
pass1?: string,
pass2?: string,
required: string,
updateMessage: string,
}

test('综合测试', async () => {
Expand Down Expand Up @@ -67,33 +68,43 @@ test('综合测试', async () => {
},
{
key: 'customSyncFn',
test: value => /abc/.test(value),
test: ({ customSyncFn }) => /abc/.test(customSyncFn!),
message: '请输入包含abc的文字',
},
{
key: 'customAsyncFn',
test: async value => {
test: async ({ customAsyncFn }) => {
await wait(500)
return /abc/.test(value)
return /abc/.test(customAsyncFn!)
},
message: '请输入包含abc的文字',
},
{
key: 'pass1',
required: true,
test: value => value.length > 6,
test: ({ pass1 }) => pass1!.length > 6,
message: '请输入大于6位的密码',
},
{
key: 'pass2',
test: (value, data) => value === data.pass1,
test: ({ pass1, pass2 }) => pass2 === pass1,
message: '请输入和密码一相同的密码',
},
{
key: 'required',
required: true,
message: '请输入',
},
{
key: 'updateMessage',
test: (_, { updateMessage }) => {
if (_.updateMessage !== 'x') {
updateMessage('出错啦')
return false
}
},
message: 'x',
},
]

const v = new EasyValidator<Data>(rules)
Expand All @@ -113,6 +124,7 @@ test('综合测试', async () => {
pass1: '1234567',
pass2: '1234567',
required: 'req',
updateMessage: 'x',
}),
{
valid: true,
Expand All @@ -135,6 +147,7 @@ test('综合测试', async () => {
pass1: '1234567',
pass2: '12345678', // 12
required: undefined as any, // 13
updateMessage: 'y', // 14
}),
{
valid: false,
Expand All @@ -145,6 +158,10 @@ test('综合测试', async () => {
rules[10],
rules[12],
rules[13],
{
...rules[14],
message: '出错啦',
},
],
},
)
Expand Down
97 changes: 74 additions & 23 deletions src/EasyValidator.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { Defined } from './enhanceType'
import { isChineseIDCardNumber, isEmail, isFunction, isInteger, isNumeric, isPossibleChineseMobilePhoneNumber, isPossibleChineseName, isPromiseLike, isRegExp, isUrl } from './is'
import { sequential } from './sequential'

export type EasyValidatorData = Record<keyof any, any>

export interface EasyValidatorRuleTestFunctionActions {
/**
* 更新提示信息。
*
* @param message 提示信息
*/
updateMessage(message: any): void,
}

export interface EasyValidatorRuleTestFunction<D extends EasyValidatorData> {
/**
* 测试函数。
* 测试函数,除非显式返回 `false`,否则都将视为测试通过
*
* @param value 要测试的值
* @param data 数据对象
* @param actions 操作列表
* @returns 返回是否测试通过
*/
(
value: Defined<D[keyof D]>,
data: D,
): boolean | Promise<boolean>,
actions: EasyValidatorRuleTestFunctionActions,
): any,
}

export interface EasyValidatorRule<D extends EasyValidatorData> {
Expand All @@ -33,7 +41,11 @@ export interface EasyValidatorRule<D extends EasyValidatorData> {
),
/** 是否必填 */
required?: boolean,
/** 自定义测试,支持正则、同步函数、异步函数 */
/**
* 自定义测试,支持正则、同步函数、异步函数。
*
* 为函数时,除非显式返回 `false`,否则都将视为测试通过。
*/
test?: (
RegExp |
EasyValidatorRuleTestFunction<D>
Expand All @@ -55,6 +67,30 @@ export interface EasyValidatorValidateReturn<D extends EasyValidatorData> {
* 数据对象验证器。
*
* @template D 要验证的数据对象类型
* @example
* ```ts
* interface Data {
* name: string,
* phoneNumber: string,
* pass1: string,
* pass2: string,
* }
* const ev = new EasyValidator<Data>([
* { key: 'name', type: 'chineseName', message: '请输入真实姓名' },
* { key: 'phoneNumber', type: 'chineseMobilePhoneNumber', message: '请输入正确的手机号码' },
* {
* key: 'phoneNumber',
* test: async (phoneNumber) => {
* const result = await checkPhoneNumberAsync(phoneNumber)
* if (result.pass) return true
*
* },
* message: '请输入正确的手机号码'
* },
* { key: 'pass1', test: pass1 => pass1.length > 6, message: '密码应大于6位' },
* { key: 'pass2', test: (pass2, data) => pass2 === data.pass1, message: '两次密码应一致' },
* ])
* ```
*/
export class EasyValidator<D extends EasyValidatorData> {
/**
Expand All @@ -65,41 +101,41 @@ export class EasyValidator<D extends EasyValidatorData> {
constructor(private rules: EasyValidatorRules<D>) {}

private check(rule: EasyValidatorRule<D>, data: D) {
return new Promise<boolean>(resolve => {
return new Promise<{ valid: boolean, message?: any }>(resolve => {
const key = rule.key
const value = data[key]

/* istanbul ignore if */
if (!(key in data)) {
if (rule.required) {
return resolve(false)
return resolve({ valid: false })
}
} else {
if ((rule.required || rule.type || rule.test) && (value == null || value === '')) {
return resolve(false)
return resolve({ valid: false })
}
if (rule.type) {
switch (rule.type) {
case 'number':
if (!isNumeric(value)) return resolve(false)
if (!isNumeric(value)) return resolve({ valid: false })
break
case 'integer':
if (!isNumeric(value) || !isInteger(Number(value))) return resolve(false)
if (!isNumeric(value) || !isInteger(Number(value))) return resolve({ valid: false })
break
case 'chineseMobilePhoneNumber':
if (!isPossibleChineseMobilePhoneNumber(value)) return resolve(false)
if (!isPossibleChineseMobilePhoneNumber(value)) return resolve({ valid: false })
break
case 'chineseIdCardNumber':
if (!isChineseIDCardNumber(value)) return resolve(false)
if (!isChineseIDCardNumber(value)) return resolve({ valid: false })
break
case 'url':
if (!isUrl(value)) return resolve(false)
if (!isUrl(value)) return resolve({ valid: false })
break
case 'email':
if (!isEmail(value)) return resolve(false)
if (!isEmail(value)) return resolve({ valid: false })
break
case 'chineseName':
if (!isPossibleChineseName(value)) return resolve(false)
if (!isPossibleChineseName(value)) return resolve({ valid: false })
break
/* istanbul ignore next */
default:
Expand All @@ -109,19 +145,30 @@ export class EasyValidator<D extends EasyValidatorData> {
if (rule.test) {
if (isRegExp(rule.test)) {
if (!rule.test.test(value)) {
return resolve(false)
return resolve({ valid: false })
}
} else if (isFunction(rule.test)) {
const result = rule.test(value, data)
let message: any
const actions: EasyValidatorRuleTestFunctionActions = {
updateMessage(comingMessage) {
message = comingMessage
},
}
const result = rule.test(data, actions)
if (isPromiseLike(result)) {
return result.then(resolve)
return result
.then(
pass => ({ valid: pass !== false, message }),
message => ({ valid: false, message }),
)
.then(resolve)
}
return resolve(result)
return resolve({ valid: result !== false, message })
}
}
}

return resolve(true)
return resolve({ valid: true })
})
}

Expand All @@ -141,10 +188,14 @@ export class EasyValidator<D extends EasyValidatorData> {
rule => () => {
return new Promise(resolve => {
if (unvalidKeys.indexOf(rule.key) === -1) {
this.check(rule, data).then(valid => {
this.check(rule, data).then(({ valid, message }) => {
if (!valid) {
unvalidKeys.push(rule.key)
unvalidRules.push(rule)
unvalidRules.push(
message == null
? rule
: { ...rule, message },
)
}
resolve()
})
Expand Down

0 comments on commit 3827577

Please sign in to comment.