跳转到内容

自定义验证

本文介绍如何在 Piying-View 中使用自定义验证器,包括同步和异步验证。

validators 接受一个验证函数数组,每个验证函数接收 AbstractControl 并返回错误或 null/undefined

输入:当前字段控件 control,通过 control.value 读当前值。

输出

  • 返回 null / undefined:验证通过
  • 返回 { [errorKey]: errorValue }:旧格式错误(向后兼容)
  • 返回 [{ kind, metadata, message? }]:新格式错误(推荐)

formConfig.asyncValidators — 异步验证器

Section titled “formConfig.asyncValidators — 异步验证器”

asyncValidators 接受异步验证函数数组,用于网络请求等耗时操作,支持 Promise/Observable/Signal:

Piying-View 同时支持 Valibot 内置验证和自定义验证器,两者并行执行

Valibot 验证和自定义验证器的错误会合并到 control.errors 中:

// 输入 "test@test.test"
control.errors = [
  { kind: 'valibot', metadata: [...] },           // Valibot 验证失败
  { kind: 'testDomain', message: '...' },        // 自定义验证器失败
];

比较两个字段是否相等(如密码确认):

也可以使用 group 只在专门的组中进行验证,通过 validGroup 组件包裹需要验证的字段:

validGroup 是一个自定义分组组件,仅对组内字段进行验证并集中显示错误。

Piying-View 的 FieldControl 提供标准接口访问验证状态:

属性 类型 说明
control.errors ValidationErrors2[] | null 当前所有错误(Valibot + 自定义)
control.valid boolean 是否所有验证都通过
control.status$$() 'VALID' | 'INVALID' | 'PENDING' 验证状态(包括异步验证中)
control.dirty boolean 值是否被修改过
control.touched boolean 是否被聚焦过
control.pristine boolean 是否未被修改过

新格式错误访问示例:

if (control.errors) {
  for (const error of control.errors) {
    if (error.kind === 'usernameTaken') {
      console.log('用户名已占用:', error.metadata?.value);
    } else if (error.kind === 'valibot') {
      // Valibot 验证失败
      const issues = error.metadata;
    }
  }
}

Piying-View 支持两种验证方式,二者并行执行

验证方式 说明 特点
Valibot 验证 在 Valibot 解析阶段进行 只能验证自身,无法以上下文值验证
控件自带验证 formConfig.validators 注册,独立执行 可访问父级,根据其他字段值判断
// Valibot 内置验证
v.pipe(v.string(), v.minLength(5));

// 自定义验证
v.pipe(
  v.string(),
  v.check((value) => value === 'k2-value', 'should input k2-value'),
);

错误合并:两种方式相互独立执行,不存在「一个失败就跳过另一个」——control.errors 中先是自定义验证器的错误,若 Valibot 验证不通过,再追加一条 { kind: 'valibot', metadata: issues } 错误。

Valibot 提供了预制的国际化支持,可按浏览器语言加载对应语言包:

import '@valibot/i18n/zh-CN';
import { setGlobalConfig } from 'valibot';

const browserLanguage = navigator.language;
if (browserLanguage.startsWith('zh')) {
  setGlobalConfig({ lang: 'zh-CN' });
}

控件自带验证的国际化需要在需要显示异常信息的包装器、组件上自行实现。