- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
导读
本文以 ng-zorro-antd 组件库中 cascader 响应式表单示例 为核心,讲解如何在 Angular Reactive Forms 中集成级联选择器(nz-cascader),涵盖formControlName双向数据绑定、FormBuilder构建表单、valueChanges值监听,以及通过表单reset()一键清空已选值等完整实操。读完本文,你将掌握级联选择器与响应式表单深度集成的标准写法,并能利用ControlValueAccessor的底层机制解释数据同步与表单状态校验的原理。
示例背景:为什么要用响应式表单操作 Cascader
nz-cascader是一个层级级联选择组件,典型应用场景是省/市/区、公司层级、分类体系等多级结构的数据选择。相比模板驱动表单,响应式表单(Reactive Forms)更适合复杂场景:
- 表单状态与值由
FormGroup/FormControl统一管理,便于在提交、重置、校验等场景下对值进行编程式操作; - 可以自由订阅
valueChanges观察值变化流,配合 RxJS 做防抖、过滤等处理; - 校验规则(如必填)声明式配置在控件上,与组件解耦。
仓库中的演示示例 reactive-form.ts 正是把nz-cascader放入formGroup中,并通过表单的reset()清空已选值——这正是本篇文章要展开的核心主题。
一、完整示例代码与逐行拆解
演示组件的完整代码如下(源自 components/cascader/demo/reactive-form.ts):
import { Component, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzCascaderModule, NzCascaderOption } from 'ng-zorro-antd/cascader'; const options: NzCascaderOption[] = [ { value: 'zhejiang', label: 'Zhejiang', children: [ { value: 'hangzhou', label: 'Hangzhou', children: [ { value: 'xihu', label: 'West Lake', isLeaf: true } ] }, { value: 'ningbo', label: 'Ningbo', isLeaf: true } ] }, { value: 'jiangsu', label: 'Jiangsu', children: [ { value: 'nanjing', label: 'Nanjing', children: [ { value: 'zhonghuamen', label: 'Zhong Hua Men', isLeaf: true } ] } ] } ]; @Component({ selector: 'nz-demo-cascader-reactive-form', imports: [ReactiveFormsModule, NzButtonModule, NzCascaderModule], template: ` <form [formGroup]="form" novalidate> <nz-cascader [nzOptions]="nzOptions" formControlName="name" /> </form> <br /> <button nz-button (click)="reset()">Reset</button> <button nz-button (click)="submit()">Submit</button> `, styles: ` button { margin-right: 8px; } ` }) export class NzDemoCascaderReactiveFormComponent { private fb = inject(FormBuilder); form = this.fb.group({ name: this.fb.control<string[] | null>(null, Validators.required) }); readonly nzOptions: NzCascaderOption[] = options; constructor() { this.form.controls.name.valueChanges.pipe(takeUntilDestroyed()).subscribe(data => { this.onChanges(data); }); } reset(): void { this.form.reset(); console.log(this.form.value); } submit(): void { console.log(this.form.value); } onChanges(values: string[] | null): void { console.log(values); } }1. 模板层:把表单控件挂到组件上
<form [formGroup]="form" novalidate> <nz-cascader [nzOptions]="nzOptions" formControlName="name" /> </form>关键点有三处:
[formGroup]="form"将FormGroup绑定到<form>元素;formControlName="name"将nz-cascader与表单中名为name的FormControl绑定,实现双向的值同步;[nzOptions]="nzOptions"传入级联数据源,类型为NzCascaderOption[]。
数据源中每层节点的关键字段(定义见 typings.ts):
| 字段 | 说明 |
|---|---|
value | 节点值,选中后写入表单控件的实际值 |
label | 展示给用户的文案 |
children | 子级节点数组,构成级联层级 |
isLeaf | 标记为叶子节点(可选,不写也能正常判定) |
disabled/disableCheckbox | 禁用节点 / 禁用勾选框(多选模式下) |
注意:示例中叶子节点显式声明了isLeaf: true,这是为了让级联菜单在点击叶子时立即收起并完成选择,无需等待「是否还有子级」的异步判断。
2. 组件类:用 FormBuilder 构建表单
private fb = inject(FormBuilder); form = this.fb.group({ name: this.fb.control<string[] | null>(null, Validators.required) });- 通过
inject(FormBuilder)依赖注入构造器(Angular 14+ 的注入方式,无需在构造函数里手动写参数); name控件类型为string[] | null,初始值为null;- 附加了
Validators.required必填校验,因此未选择任何级联项时,该控件处于INVALID状态,可用于表单错误提示与提交拦截。
3. 监听值变化
constructor() { this.form.controls.name.valueChanges.pipe(takeUntilDestroyed()).subscribe(data => { this.onChanges(data); }); }- 订阅
name控件的valueChanges,每次用户选择/清空级联项都会触发回调,演示中仅打印到控制台; takeUntilDestroyed()来自@angular/core/rxjs-interop,在组件销毁时自动退订,避免内存泄漏,是 Angular 16+ 推荐的替代手动ngOnDestroy退订的方式。
4. 重置与提交
reset(): void { this.form.reset(); console.log(this.form.value); } submit(): void { console.log(this.form.value); }- Reset(核心诉求):调用
this.form.reset()会把表单所有控件恢复到初始值(即null),级联选择器随即被清空,这正是该示例要演示的「通过表单重置功能清空已选值」; - Submit:打印当前表单值,实际项目中可在此处做提交校验,例如
form.valid为假时阻止提交。
二、底层原理:Cascader 如何实现与表单控件的双向同步
nz-cascader之所以能直接用在formControlName/ngModel上,是因为组件实现了 Angular 表单的ControlValueAccessor接口。这在 cascader.component.ts 中有明确声明:
export class NzCascaderComponent extends NzTreeBase implements NzCascaderComponentAsSource, OnInit, OnChanges, ControlValueAccessor并在组件元数据的providers中注册:
providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NzCascaderComponent), multi: true }, ... ](见 cascader.component.ts,配合forwardRef解决类声明顺序导致的循环引用问题。)
ControlValueAccessor 四个核心方法
组件实现了接口要求的全部方法(源码见 cascader.component.ts 与 L1125-L1132):
| 方法 | 作用 | Cascader 实现要点 |
|---|---|---|
writeValue(value) | 外部(表单)向组件写入值 | 非空时把值写入cascaderService.values并重建选中节点;为null/undefined/[]时清空values与selectedNodes并触发重绘 |
registerOnChange(fn) | 注册值变化回调 | 保存fn,内部通过emitValue调用,把选中值推送给表单 |
registerOnTouched(fn) | 注册失焦回调 | 触发下拉交互(如点击触发器)时调用onTouched(),用于标记控件 touched 状态 |
setDisabledState(isDisabled) | 响应禁用状态 | 设置nzDisabled,并关闭已打开的下拉菜单 |
值写入与输出路径
- 表单 → 组件:
form.reset()将控件值置为null后,Angular 调用writeValue(null),组件进入「空值分支」:cascaderService.values = []、clearSelectedNodes()、selectedNodes = [],并触发$redraw重绘,界面上即清空所有已选标签与选中态。 - 组件 → 表单:用户点击叶子节点完成选择后,
emitValue(values)依据单选/多选模式把值数组(或多选时的数组集合)通过registerOnChange注册的回调推送给FormControl,从而触发valueChanges流与校验重算。
值得注意的是emitValue的细节(cascader.component.ts):
emitValue(values: NzSafeAny[] | null): void { if (this.nzMultiple) { this.onChange(values); } else { this.onChange(values?.length ? values[0] : []); } }单选模式下,表单控件拿到的是「从根到叶的 value 路径数组」(如['zhejiang', 'hangzhou', 'xihu']);多选模式下则是一组这样的路径。这也是示例中把控件类型声明为string[] | null的原因。
三、校验联动:必填校验与错误状态
给name控件添加Validators.required后,表单框架会自动把校验结果反馈到级联组件上。这一机制在测试用例 cascader.spec.ts 中有完整验证:
formGroup.controls.demo.markAsDirty(); formGroup.controls.demo.setValue(null); formGroup.controls.demo.updateValueAndValidity(); fixture.detectChanges(); // show error —— 组件根元素出现 ant-select-status-error,并渲染错误反馈图标对应测试组件写法(见 cascader.spec.ts):
@Component({ imports: [ReactiveFormsModule, NzFormModule, NzCascaderModule], template: ` <form nz-form [formGroup]="validateForm"> <nz-form-item> <nz-form-control nzHasFeedback> <nz-cascader formControlName="demo" [nzOptions]="nzOptions" /> </nz-form-control> </nz-form-item> </form> ` }) export class NzDemoCascaderInFormComponent { private fb = inject(FormBuilder); validateForm = this.fb.group({ demo: this.fb.control<string[] | null>(null, Validators.required) }); }由此可以总结出响应式表单下的校验使用模式:
- 在
FormControl上声明校验器(如Validators.required); - 将级联组件置于
<nz-form-item>/<nz-form-control>中,组件内部通过NzFormStatusService订阅表单状态变化(源码见 cascader.component.ts),自动为根元素切换ant-select-status-error/ant-select-status-success等状态类; - 开启
nzHasFeedback时,会在箭头区渲染状态反馈图标(源码见组件模板中的nz-form-item-feedback-icon,cascader.component.ts)。
四、围绕「重置清空」的边界行为说明
form.reset()之所以能彻底清空级联选择器,与writeValue对空值的三态处理密切相关。测试用例 cascader.spec.ts 覆盖了多种写入值:
control.writeValue(null); // 清空,getSubmitValue().length === 0 control.writeValue(undefined); // 清空 control.writeValue([]); // 清空 control.writeValue(['zhejiang', 'hangzhou', 'xihu']); // 选中路径,按 label 拼接展示结合源码可以确认以下事实:
null、undefined、空数组[]三种取值都会触发清空逻辑,getSubmitValue()返回空数组;- 传入有效路径数组后,组件会沿路径逐级激活节点、回溯选中态,并渲染为
Zhejiang / Hangzhou / West Lake这样的面包屑式展示; - 若传入的值在选项中不存在(例如先给值后清空
nzOptions),组件仍会保留该值对应的展示文本(getLabelText()依旧输出路径),但无法重新激活对应节点——因此实际项目中应保证选项数据与控件值的一致性。
五、将示例迁移到真实业务表单的完整建议
把演示代码落地到业务中,通常会加入以下能力:
- 数据源替换:把静态
options换成接口返回的树形数据;若数据是懒加载的,可使用[nzLoadData]实现按需加载子级(此时控件初始值若为路径数组,组件会在writeValue阶段自动沿路径逐级触发加载,见 cascader.component.ts 的加载逻辑)。 - 提交校验:
submit()中先判断form.valid,为空时提示用户选择。 - 重置范围控制:若表单包含多个字段,只想清空级联控件,可改为
form.controls.name.reset();form.reset()则会重置整个FormGroup。 - 值监听扩展:
valueChanges流可与distinctUntilChanged()、debounceTime()等 RxJS 操作符组合,用于级联条件联动(如根据所选省份过滤后续列表)。
另外,若使用模板驱动表单,级联组件同样通过同一个ControlValueAccessor支持[(ngModel)]双向绑定(API 表中[ngModel]即为此用途,见 组件文档),两种表单范式可自由切换。
六、模块引入与更多参考
使用前确保已引入级联模块。组件库在 v17+ 采用 standalone 结构,cascader.module.ts 中NzCascaderModule仅转发导出组件;在响应式表单场景下,只需:
- 在组件/模块
imports中加入ReactiveFormsModule(或FormsModule)与NzCascaderModule(见示例组件imports: [ReactiveFormsModule, NzButtonModule, NzCascaderModule])。
想继续深入了解级联组件的其他能力,可查阅:
- 组件完整 API 表格:components/cascader/doc/index.en-US.md,覆盖
nzMultiple、nzShowSearch、nzPlacement、nzSize、nzVariant、nzStatus等全部输入输出; - 其他使用场景示例:cascader 演示目录,包括基本用法、懒加载、多选、搜索、自定义渲染、默认值回填等 20+ 个 demo;
- 组件核心实现:cascader.component.ts,重点关注
writeValue/emitValue/getSubmitValue三段与表单交互直接相关的代码; - 表单集成与校验测试:cascader.spec.ts,包含「In form」专项测试套件。
总结
响应式表单与nz-cascader的集成并不复杂,核心就三件事:用formControlName建立绑定、用FormBuilder声明控件与校验、用form.reset()清空值。理解背后的ControlValueAccessor机制后,你还能自由驾驭默认值回填、动态数据、校验状态联动等进阶场景——这正是「重置清空已选值」这一简单需求背后完整的工程语义。
- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
相关推荐
终极指南:如何使用ng-zorro-antd构建动态响应式表单
终极指南:如何使用ng zorro antd构建动态响应式表单 ng zorro antd是基于Ant Design设计规范开发的Angular UI组件库,提
UI组件前端Terragrunt 实战指南:用编排工具管理大规模 Terraform 项目
Terragrunt 实战指南:用编排工具管理大规模 Terraform 项目 Terragrunt 是一款开源的基础设施即代码编排工具,套在 Terrafor
CLIDevOps云原生如何在Mac上专业安装Microsoft Office并优化性能:完整实战指南
如何在Mac上专业安装Microsoft Office并优化性能:完整实战指南 Microsoft Office for macOS安装与优化解决方案为Mac用
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考