ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TanStack Form 多步骤向导实战:基于 Angular 与 FormGroup 的分步表单实现

TanStack Form 多步骤向导实战:基于 Angular 与 FormGroup 的分步表单实现 TanStack Form 多步骤向导实战基于 Angular 与 FormGroup 的分步表单实现【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form导读本文以仓库内的 examples/angular/multi-step-wizard 示例为主线讲解如何在 Angular 应用中用 TanStack Form 构建一个两步走的多步骤向导Multi-Step Wizard表单。你将掌握injectForm全局表单、injectWithForm局部表单注入、TanStackFormGroup分组提交校验、TanStackAppField字段指令以及revalidateLogic与onDynamic动态校验策略的组合用法并了解每一步校验失败时如何阻止进入下一步。读完本文后你可以直接复刻出具备分步校验 单次最终提交能力的向导表单。示例概览一个最小的两步向导示例项目位于 examples/angular/multi-step-wizard使用 Angular CLI 生成package.json中angular/cli为^21.2.12核心依赖为tanstack/angular-form: ^1.33.5与zod: ^3.25.76。目录结构如下examples/angular/multi-step-wizard/src/app/ ├── app.component.ts # 根组件切换步骤、持有全局表单 ├── shared-form.ts # 共享的 formOptions 与 zod schema ├── step1.component.ts # 第一步FormGroup step1 ├── step2.component.ts # 第二步FormGroup step2 └── text-field.component.ts # 可复用字段组件tanstack-app-field整个表单只有两个步骤Step1 / Step2每步一个name字段逻辑非常精简非常适合作为理解 FormGroup 分步提交的入门样例。注意该 README 自身只是 Angular CLI 的默认模板说明ng serve、ng build、ng test等真正的技术核心全部在src/app/的五个 TypeScript 组件中下文将以此为据展开。第一步用 formOptions 定义共享表单配置shared-form.ts 是向导表单的单一事实来源它做了两件事用 zod 定义了两个步骤各自的校验 schema注意两处的name长度要求不同Step1 至少 2 个字符Step2 至少 3 个字符用于演示每步独立校验用formOptions声明表单的defaultValues将数据按步骤分区import { formOptions } from tanstack/angular-form import { z } from zod export const step1Schema z.object({ name: z.string().min(2, Name must be at least 2 characters), }) export const step2Schema z.object({ name: z.string().min(3, Name must be at least 3 characters), }) export const wizardFormOpts formOptions({ defaultValues: { step1: { name: }, step2: { name: }, }, })关键点在于表单数据不是当前步骤的数据而是全部步骤的数据集合{ step1: {...}, step2: {...} }。这样设计使得整个向导最终可以一次性提交完整数据而不是每步独立提交后再拼接。第二步用 injectForm 创建全局表单并绑定动态校验根组件 app.component.ts 使用injectForm创建全局FormApi实例。injectForm的底层实现位于 packages/angular-form/src/inject-form.ts它内部new FormApi(opts)创建表单实例并额外injectStore(api.store, (state) state.isSubmitting)订阅isSubmitting状态使其可以在 Angular 变更检测中响应。form injectForm({ ...wizardFormOpts, validationLogic: revalidateLogic(), validators: { onDynamic: z.object({ step1: step1Schema, step2: step2Schema, }), }, onSubmit: ({ value }) { alert(Form submitted: ${JSON.stringify(value)}) }, })这里有两处容易困惑的设计源码注释解释得很清楚onDynamic只在调用form.handleSubmit()时才会被触发。当用户点击下一步触发的是 FormGroup 的handleSubmit()它只校验当前步骤的 schema不会触发这个全局onDynamic只有走到最后一步、由 Step2 调用整个form.handleSubmit()时全局的onDynamic包含两个步骤的 zod 对象才会对完整数据做最终校验。validationLogic: revalidateLogic()控制重新校验策略。查看 packages/form-core/src/ValidationLogic.tsrevalidateLogic的默认参数为{ mode: submit, modeAfterSubmission: change }表单尚未提交时change事件不触发校验一旦发生过一次提交之后在change时就会重新校验。这可以避免用户在向导中还没走到某一步就收到红字报错同时保证提交过一次后错误能即时更新。模板部分用if (step() 0)/if (step() 1)按信号step切换渲染 Step1 / Step2并通过[form]form、[step]step()、[isSubmitting]isSubmitting()传入子组件step signal(0) isSubmitting injectStore(this.form, (state) state.isSubmitting)isSubmitting用injectStore从表单 store 中选出作为提交按钮的禁用依据。第三步用 TanStackFormGroup 实现每步独立提交校验TanStackFormGroup是本示例的灵魂——它把整个表单的某个子路径如step1、step2提升为一个组组拥有自己的校验器、错误地图errorMap/errors与提交钩子。查看 step1.component.tsng-container [tanstackFormGroup]withForm.form namestep1 [validators]{ onDynamic: step1Schema } [onGroupSubmit]onGroupSubmit [onGroupSubmitInvalid]onGroupSubmitInvalid #groupformGroup form (submit) $event.preventDefault(); $event.stopPropagation(); group.api.handleSubmit() app-text-field labelStep 1 Name tanstack-app-field [tanstackField]withForm.form namestep1.name / button typesubmit [disabled]isSubmitting()Submit/button pre{{ stringify(group.api.state.meta.errorMap) }}/pre /form /ng-container组件类侧则通过injectWithForm拿到同一个FormApiwithForm.form并用stepChange输出事件告知父组件切换步骤withForm injectWithForm({ ...wizardFormOpts }) onGroupSubmit () { this.stepChange.emit(this.step() 1) } onGroupSubmitInvalid () { // 组级也能处理校验失败阻止进入下一步 }需要明确的关键机制injectWithForm定义于 packages/angular-form/src/with-form-injectable.ts经由 packages/angular-form/src/index.ts 导出它让每个步骤组件都注入同一个表单实例从而共享同一份数据与状态组的校验器[validators]{ onDynamic: step1Schema }只作用于本组数据step1分支提交时调用的是group.api.handleSubmit()组的提交方法它只校验当前组校验通过才回调onGroupSubmit这里实现进入下一步校验失败则回调onGroupSubmitInvalid——这个钩子正是阻止向导前进到非法步骤的关键示例中留空注释说明了其用途组同样拥有state.meta.errorMap与表单、字段一致可直接在模板中展示。Step2 组件step2.component.ts结构相同唯一区别是校验器换成step2Schemaname至少 3 个字符提交成功后不再stepChange.emit(step()1)而是调用全局this.withForm.form.handleSubmit()从而触发根组件中定义的全局onDynamic校验step1 step2 一起与onSubmit完成向导的最终提交提供 Back 按钮(click)stepChange.emit(step() - 1)返回上一步。第四步用 TanStackAppField 封装可复用字段text-field.component.ts 把标签 输入框 错误列表封装成一个可复用字段组件并通过injectField注入字段 APIComponent({ selector: app-text-field, standalone: true, template: div label div{{ label() }}/div input [value]field.api.state.value (input)field.api.handleChange($any($event).target.value) (blur)field.api.handleBlur() / /label for (error of field.api.state.meta.errors; track $index) { div stylecolor: red{{ error.message }}/div } /div , }) export class TextFieldComponent { label input.requiredstring() field injectFieldstring() }其使用方式为在宿主元素上同时加tanstack-app-field指令与[tanstackField]withForm.form、namestep1.name。TanStackAppField指令定义于 packages/angular-form/src/app-field.ts它通过DeepKeysTParentData/DeepValueTParentData, TName对name做类型约束保证step1.name这类深路径在编译期就是合法的从而让整个向导表单保持类型安全。字段的错误信息统一取自field.api.state.meta.errorszod 校验产生的 message 会直接渲染为红色提示。完整工作流串联把四个文件串起来向导的运行流程如下初始状态根组件step signal(0)只渲染app-step1Step1 提交用户点击 Submitgroup.api.handleSubmit()触发——TanStackFormGroup的onDynamic: step1Schema校验step1.name失败 → 触发onGroupSubmitInvalid步骤不切换错误显示在字段下方通过 → 触发onGroupSubmitstepChange.emit(1)根组件切到app-step2Step2 提交同样先由step2Schema校验step2.name失败 → 停留本步通过 →withForm.form.handleSubmit()调用全局handleSubmit触发全局onDynamic对{step1, step2}整体再校验一次→ 全部通过后执行onSubmitalert输出完整 JSON随时返回Step2 的 Back 按钮发出stepChange.emit(0)回到 Step1已填数据因共享同一FormApi而完整保留。运行示例在 examples/angular/multi-step-wizard 目录下需先安装依赖项目为 pnpm workspace 的一部分见根目录 pnpm-workspace.yamlnpm install # 或 pnpm install npm start # 等价于 ng cache clean ng serve默认开发服务器地址为http://localhost:4200/修改源码后会自动热重载。生产构建使用npm run build即ng build产物输出到dist/目录单元测试用npm testng test基于 Karma。延伸阅读若想深入了解表单分组 API 的类型与状态结构可阅读 FormGroupApi 文档 与 FormGroupState 接口文档全局表单的完整选项onDynamic、onSubmit、validationLogic等见 FormOptions 文档revalidateLogic的默认值与运行逻辑见 ValidationLogic 源码 与 revalidateLogic 文档其他框架React / Vue / Solid / Lit / Svelte均有对应的 multi-step-wizard 示例可参照对比例如 examples/react/multi-step-wizard。【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表