- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
本文以 ng-zorro-antd 官方演示Dynamic Settings(动态控制表格属性)为核心,完整拆解其"表单配置面板 + 响应式表格渲染"的实现方案。演示位于 components/table/demo/dynamic-settings.md(说明文档)与 components/table/demo/dynamic-settings.ts(源码实现),读者读完可以掌握如何通过ReactiveFormsModule的FormGroup与 Angular signals 驱动nz-table的十余个输入属性,在运行期实时切换边框、分页、固定列、表头、加载态、尺寸等配置,并独立复刻出这一"配置组合预览器"。
一、演示主题:选择不同配置组合查看效果
官方对该演示的定位非常精炼,中文说明为"选择不同配置组合查看效果",英文为"Select different settings to see the result"。虽然说明只有一句话,但其背后的实现承载了nz-table绝大多数可动态调节的属性:
- 外观类:
bordered(边框)、size(表格尺寸)、loading(加载态); - 结构类:
title(标题)、header(列头)、footer(表尾)、expandable(展开行); - 数据类:
pagination(前端分页开关)、sizeChanger(每页条数切换器)、simple(简单分页)、position(分页位置)、paginationType(分页尺寸); - 交互类:
checkbox(行多选)、ellipsis(单元格省略)、fixHeader(固定表头)、tableScroll(横向/固定滚动与固定列)。
整个演示可以看作一个"表格属性实验台":左侧(上方)是一排开关与单选组,右侧(下方)是实时响应这些设置的表格。下面从源码逐层还原其实现。
二、配置面板的骨架:Setting 接口与表单初始化
演示把所有可配置项收敛进一个强类型接口Setting,定义见 components/table/demo/dynamic-settings.ts:
type TableScroll = 'unset' | 'scroll' | 'fixed'; interface Setting { bordered: boolean; loading: boolean; pagination: boolean; sizeChanger: boolean; title: boolean; header: boolean; footer: boolean; expandable: boolean; checkbox: boolean; fixHeader: boolean; noResult: boolean; ellipsis: boolean; simple: boolean; size: NzTableSize; tableScroll: TableScroll; tableLayout: NzTableLayout; position: NzTablePaginationPosition; paginationType: NzTablePaginationType; }这些枚举类型全部来自组件自身的类型定义 components/table/src/table.types.ts:
export type NzTableLayout = 'fixed' | 'auto'; export type NzTablePaginationPosition = 'top' | 'bottom' | 'both'; export type NzTablePaginationType = 'default' | 'small'; export type NzTableSize = 'middle' | 'default' | 'small';也就是说:表格尺寸只有middle / default / small三种;分页位置可选top / bottom / both;分页类型仅default / small;表格布局仅fixed / auto。配置面板中"Size、Pagination Position、Pagination Type、Table Layout"四组单选的可选值,正是这些类型的字面量全集。
表单通过NonNullableFormBuilder构造,并为每个控件给出演示用的默认值(源码第 155-174 行):
readonly settingForm: FormGroup<{ [K in keyof Setting]: FormControl<Setting[K]> }> = this.formBuilder.group({ bordered: [false], loading: [false], pagination: [true], sizeChanger: [false], title: [true], header: [true], footer: [true], expandable: [true], checkbox: [true], fixHeader: [false], noResult: [false], ellipsis: [false], simple: [false], size: 'small' as NzTableSize, paginationType: 'default' as NzTablePaginationType, tableScroll: 'unset' as TableScroll, tableLayout: 'auto' as NzTableLayout, position: 'bottom' as NzTablePaginationPosition });三、面板 UI:开关组与单选组的声明式渲染
面板模板使用@for循环统一渲染两组控件(源码第 54-77 行):
listOfSwitch:13 个nz-switch,每个开关绑定一个布尔型formControlName,例如Bordered、Loading、Pagination、PageSizeChanger、Title、Column Header、Footer、Expandable、Checkbox、Fixed Header、No Result、Ellipsis、Simple Pagination;listOfRadio:5 组nz-radio-group(按钮样式nz-radio-button),对应Size、Table Scroll、Table Layout、Pagination Position、Pagination Type。
<form nz-form nzLayout="inline" [formGroup]="settingForm"> @for (item of listOfSwitch; track item) { <nz-form-item> <nz-form-label>{{ item.name }}</nz-form-label> <nz-form-control> <nz-switch [formControlName]="item.formControlName" /> </nz-form-control> </nz-form-item> } @for (radio of listOfRadio; track radio) { <nz-form-item> <nz-form-label>{{ radio.name }}</nz-form-label> <nz-form-control> <nz-radio-group [formControlName]="radio.formControlName"> @for (o of radio.listOfOption; track o) { <label nz-radio-button [nzValue]="o.value">{{ o.label }}</label> } </nz-radio-group> </nz-form-control> </nz-form-item> } </form>这种"配置元数据数组"的做法把 13 个开关、5 组单选收敛成两份纯数据,模板零重复,新增配置项只需往数组里加一条记录——这是本演示最值得借鉴的工程模式。
四、表格模板:nz-table 输入属性与单元格增强语法
表格部分(源码第 78-143 行)把settingForm的当前值经settingValue()signal 逐项绑定到nz-table:
<nz-table #dynamicTable [nzScroll]="{ x: scrollX(), y: scrollY() }" [nzData]="listOfData()" [nzTableLayout]="settingValue().tableLayout" [nzBordered]="settingValue().bordered" [nzSimple]="settingValue().simple" [nzLoading]="settingValue().loading" [nzPaginationType]="settingValue().paginationType" [nzPaginationPosition]="settingValue().position" [nzShowSizeChanger]="settingValue().sizeChanger" [nzFrontPagination]="settingValue().pagination" [nzShowPagination]="settingValue().pagination" [nzFooter]="settingValue().footer ? 'Here is Footer' : null" [nzTitle]="settingValue().title ? 'Here is Title' : null" [nzSize]="settingValue().size" (nzCurrentPageDataChange)="currentPageDataChange($event)" >各输入属性在官方 API 文档 components/table/doc/index.zh-CN.md 与 components/table/doc/index.en-US.md 中有完整定义,与本演示的对应关系如下:
| 演示控件 | nz-table 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
| Bordered | [nzBordered] | boolean | false | 是否展示外边框和列边框 |
| Loading | [nzLoading] | boolean | false | 页面是否加载中(可配nzLoadingDelay防闪烁) |
| Pagination | [nzFrontPagination]/[nzShowPagination] | boolean | true/true | 是否前端分页、是否显示分页器 |
| PageSizeChanger | [nzShowSizeChanger] | boolean | false | 是否可以改变nzPageSize(选项默认[10, 20, 30, 40]) |
| Title | [nzTitle] | string \| TemplateRef<void> | - | 表格标题 |
| Footer | [nzFooter] | string \| TemplateRef<void> | - | 表格尾部 |
| Fixed Header | [nzScroll].y | object | - | 纵向滚动即固定表头 |
| No Result | [nzData]置空 | T[] | - | 无数据时展示空状态 |
| Ellipsis | [nzEllipsis](th/td) | boolean | false | 单元格内容省略,仅在nzTableLayout = 'fixed'时生效 |
| Simple Pagination | [nzSimple] | boolean | - | 简单分页 |
| Size | [nzSize] | 'middle' \| 'small' \| 'default' | 'default' | 表格尺寸 |
| Table Layout | [nzTableLayout] | 'fixed' \| 'auto' | 'auto' | 表格布局,fixed是nzEllipsis生效的前提 |
| Pagination Position | [nzPaginationPosition] | 'top' \| 'bottom' \| 'both' | 'bottom' | 分页显示位置 |
| Pagination Type | [nzPaginationType] | 'default' \| 'small' | 'default' | 分页尺寸 |
注意[nzTitle]与[nzFooter]的写法:settingValue().title ? 'Here is Title' : null,即传入null时关闭对应区域,传入字符串时渲染文本(二者也支持TemplateRef自定义复杂内容)。
单元格层面用到了三组增强指令(演示第 96-142 行):
- 固定列:
[nzLeft]="fixedColumn()"(左侧固定列)与[nzRight]="fixedColumn()"(右侧固定 Action 列),fixedColumn()为boolean时由组件自动计算像素宽度; - 展开行:
<td [(nzExpand)]="data.expand">双向绑定展开状态,配合<tr [nzExpand]="data.expand">渲染展开内容; - 行多选:表头
[nzChecked]="allChecked()"、[nzIndeterminate]="indeterminate()"与(nzCheckedChange)="checkAll($event)",行单元格[(nzChecked)]="data.checked"双向绑定。
五、数据与选择状态管理:signals + 当前页数据回填
演示用 Angular 16+ 的 signal 承载全部运行期状态(源码第 175-182 行):
readonly listOfData = signal<readonly ItemData[]>([]); // 全量数据 readonly displayData = signal<readonly ItemData[]>([]); // 当前页数据 readonly allChecked = signal(false); // 表头全选状态 readonly indeterminate = signal(false); // 半选状态 readonly fixedColumn = signal(false); // 是否固定首尾列 readonly scrollX = signal<string | null>(null); // 横向滚动宽度 readonly scrollY = signal<string | null>(null); // 纵向滚动高度 readonly settingValue = signal(this.settingForm.value as Setting); // 配置快照关键联动是(nzCurrentPageDataChange)事件:表格当前页数据变化时写回displayData并刷新勾选状态(源码第 244-255 行):
currentPageDataChange($event: readonly ItemData[]): void { this.displayData.set($event); this.refreshStatus(); } refreshStatus(): void { const validData = this.displayData().filter(value => !value.disabled); const allChecked = validData.length > 0 && validData.every(value => value.checked); const allUnChecked = validData.every(value => !value.checked); this.allChecked.set(allChecked); this.indeterminate.set(!allChecked && !allUnChecked); }全选/取消全选只作用于当前页displayData(并跳过disabled行),这是分页表格多选的正确姿势——避免跨页误操作;indeterminate半选状态在"部分选中且部分未选"时为true:
checkAll(value: boolean): void { this.displayData().forEach(data => { if (!data.disabled) { data.checked = value; } }); this.refreshStatus(); }测试数据由generateData()生成 100 条记录(源码第 266-280 行),字段覆盖演示所需:checked(勾选)、expand(展开)、description(展开内容)、age/address(普通单元格,Address 用于验证nzEllipsis省略效果)。
六、联动订阅:tableScroll、fixHeader 与 noResult 的副作用
演示最具"动态"精髓的是三个valueChanges订阅(源码第 282-301 行),它们把表单控件的变化翻译成表格的具体行为:
ngOnInit(): void { // 1. 全量配置快照:任何表单变化都刷新 settingValue this.settingForm.valueChanges.subscribe(value => { this.settingValue.set(value as Setting); }); // 2. Table Scroll 三态联动:unset / scroll / fixed this.settingForm.controls.tableScroll.valueChanges.subscribe(scroll => { this.fixedColumn.set(scroll === 'fixed'); // fixed 时固定左右列 this.scrollX.set(scroll === 'scroll' || scroll === 'fixed' ? '100vw' : null); // 横向滚动 }); // 3. Fixed Header:开启后给表格纵向滚动高度 240px,即固定表头 this.settingForm.controls.fixHeader.valueChanges.subscribe(fixed => { this.scrollY.set(fixed ? '240px' : null); }); // 4. No Result:清空/恢复数据以演示空状态 this.settingForm.controls.noResult.valueChanges.subscribe(empty => { if (empty) { this.listOfData.set([]); } else { this.listOfData.set(this.generateData()); } }); this.listOfData.set(this.generateData()); }其中第 2 处值得展开:Table Scroll单选有Unset / Scroll / Fixed三档——scroll仅开启横向滚动(nzScroll.x = '100vw'),fixed在横向滚动基础上再通过[nzLeft]/[nzRight]固定 Name 列与 Action 列,形成"横向滚动 + 首尾固定列"的经典复杂表格布局;而nzScroll.y = '240px'固定表头则被独立成Fixed Header开关,二者互不干扰、可自由组合。
另外注意settingForm的泛型写法FormGroup<{ [K in keyof Setting]: FormControl<Setting[K]> }>——它利用映射类型让每个控件的类型与Setting字段严格对齐,是 TypeScript 强类型表单的模板级示范;NonNullableFormBuilder保证默认值非空。
七、性能与变更检测提示
官方文档 components/table/doc/index.en-US.md 的 Note 一节专门提醒:NG-ZORRO 组件全部运行在 OnPush 变更检测策略下,直接修改传入@Input()的数组/对象不会触发视图更新,应使用不可变方式更新数据(演示中正是依赖signal.set()与@for ... track data.id保证引用变更可被检测):
// 新增数据:使用展开运算符生成新数组 this.dataSet = [...this.dataSet, newItem]; // 删除数据:使用 filter 生成新数组 this.dataSet = this.dataSet.filter(d => d.key !== key);八、如何在本仓库中复现与继续研究
- 演示说明:查看 components/table/demo/dynamic-settings.md;
- 完整实现:阅读 components/table/demo/dynamic-settings.ts,其中组件依赖
ReactiveFormsModule、NzFormModule、NzRadioModule、NzSwitchModule、NzDividerModule与NzTableModule,引入这些模块后即可在任意页面复刻; - 类型定义:
NzTableSize、NzTableLayout、NzTablePaginationPosition、NzTablePaginationType等枚举见 components/table/src/table.types.ts; - API 全表:
nz-table、th、td、thead、tr、tfoot的全部输入输出属性见 components/table/doc/index.zh-CN.md 与 components/table/doc/index.en-US.md; - 组件核心实现:
nz-table主体位于components/table/src/table/目录(含table.component.ts、table-inner-default.component.ts等),table-data.service.ts负责分页/排序/筛选的数据加工管线,是理解"前端分页nzFrontPagination"底层机制的最佳入口。
小结
Dynamic Settings演示虽小,却是理解nz-table输入属性的"活字典":一份Setting接口、一份表单、两份配置元数据数组、四条valueChanges订阅,就串起了边框、加载、分页、尺寸、布局、滚动、固定列/表头、展开行、多选、省略、空状态等几乎全部核心能力。把它的"配置元数据 + 强类型表单 + signal 快照"模式迁移到自己的业务中,即可低成本打造"表格设置中心"或调试面板,快速验证任意属性组合下的表现。
- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
相关推荐
ng-zorro-antd Table 可控排序与筛选:用 `[nzSortOrder]` / `[nzFilters]` 编程式控制表格状态
ng zorro antd Table 可控排序与筛选:用 nzSortOrder / nzFilters 编程式控制表格状态 本篇指南聚焦 ng zorro
UI组件前端ant-design Table 动态配置实战:基于 Dynamic Settings 示例实现可交互的表格属性控制面板
ant design Table 动态配置实战:基于 Dynamic Settings 示例实现可交互的表格属性控制面板 导读 本文以 ant design 仓
前端UI组件设计系统ng-zorro-antd 表格嵌套子表格(Nested Table)实战指南:用 nzExpand 展开行展示每行详情
ng zorro antd 表格嵌套子表格(Nested Table)实战指南:用 nzExpand 展开行展示每行详情 嵌套子表格(Nested tables
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考