如何在 Angular Signal Forms 中使用 Zod(逐步迁移)
Source: Dev.to
问题描述:Signal Forms 迁移后需要重新集成 Zod 验证
许多开发者都会遇到同样的情况:表单在使用 Reactive Forms 和 Zod 验证时工作正常,但迁移到 Signal Forms 后验证失效。即使字段无效,表单仍会提交,错误信息也会消失。
需要使用 Signal Forms 的验证系统重新集成 Zod。我们先了解涉及的各个部分,然后正确地完成迁移。
什么是 Zod?为什么 Angular 开发者用它来做验证
如果你对 Zod 还不熟悉,这里有一个快速概览:
- 类型安全 – TypeScript 类型与验证 schema 保持同步。
- 运行时验证 – 捕获 TypeScript 编译时无法捕获的错误。
- 简洁的错误信息 – 开箱即用的人类可读的验证错误。
- 可组合的 schema – 可以用简单的构件块构建复杂的验证规则。
对于 Angular 开发者来说,Zod 将验证逻辑集中在 Angular 表单系统之外,便于在前后端之间共享规则或在应用的不同模块中复用 schema。
像其他 npm 包一样安装 Zod:
npm install zod
(假设已经安装好,我们可以直接进入代码。)
理解用于 Angular 表单验证的 Zod Schema
Schema 文件 (form.schema.ts)
import { z } from 'zod';
类型推断
export type SignupModel = z.infer;
错误映射类型
export type ZodErrorMap = Record;
验证 schema
export const signupSchema = z.object({
username: z
.string()
.min(3, 'Username must be at least 3 characters long')
.regex(
/^[a-zA-Z0-9_]+$/,
'Only letters, numbers, and underscores are allowed'
),
email: z.string().email('Please enter a valid email address'),
});
验证辅助函数
export function validateSignup(value: SignupModel) {
const result = signupSchema.safeParse(value);
if (result.success) {
return {
success: true as const,
data: result.data,
errors: {} as ZodErrorMap,
};
}
const errors = result.error.issues.reduce((acc, issue) => {
const field = issue.path[0]?.toString() ?? '_form';
(acc[field] ??= []).push(issue.message);
return acc;
}, {});
return {
success: false as const,
data: null,
errors,
};
}
validateSignup 函数返回成功的结果(包含已验证的数据)或失败的结果(包含干净的 ZodErrorMap,我们可以在 UI 中展示)。
Zod 在 Angular Reactive Forms 中的接入方式(迁移前)
模板(Reactive Forms)
@let usernameErrors = getZodErrors('username');
@if (usernameErrors.length && form.controls.username.touched) {
@for (err of usernameErrors; track $index) {
- {{ err }}
}
}
@let emailErrors = getZodErrors('email');
@if (emailErrors.length && form.controls.email.touched) {
@for (err of emailErrors; track $index) {
- {{ err }}
}
}
<button type="submit">Submit</button>
formGroup将模板绑定到 Reactive Form 实例。formControlName把每个输入框连接到对应的FormControl。getZodErrors(fieldName)用来获取指定字段的 Zod 错误信息数组。- 只有在字段已被触碰且存在错误信息时才显示错误。
组件 TypeScript:Reactive Forms 实现方式
import { Component, inject } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ZodErrorMap, validateSignup, SignupModel } from './form.schema';
@Component({
selector: 'app-signup',
templateUrl: './form.component.html',
})
export class FormComponent {
private fb = inject(FormBuilder);
// 存放 Zod 验证错误
zodErrors: ZodErrorMap = {};
// Reactive Form 定义
form = this.fb.group({
username: ['', [Validators.required]],
email: ['', [Validators.required, Validators.email]],
});
// 获取特定字段错误的辅助方法
getZodErrors(field: keyof SignupModel): string[] {
return this.zodErrors[field] ?? [];
}
// 提交处理函数
onSubmit(): void {
const value = this.form.value as SignupModel;
const result = validateSignup(value);
if (result.success) {
// 处理成功提交(例如发送到服务器)
console.log('Form data is valid:', result.data);
} else {
// 将 Zod 错误填充到模板可显示的对象中
this.zodErrors = result.errors;
// 可选:标记所有控件为 touched,以触发 UI 反馈
this.form.markAllAsTouched();
}
}
}
zodErrors保存validateSignup返回的错误映射。getZodErrors在模板中用于获取字段对应的错误信息。- 提交时,组件使用 Zod 验证表单数据,更新
zodErrors,并将控件标记为 touched,以确保错误信息出现。
接下来的步骤(此处未展示)将用 Angular Signal Forms 替换 Reactive Form 的实现,使用 validateTree() 将 Zod 的错误映射桥接到 Signal Forms 的验证 API 中。