- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
标签页(Tabs)是 NG-ZORRO 中最常用的导航类组件之一,用于在平级区域内收纳和展现大块内容。本文以 components/tabs/doc/index.zh-CN.md 官方文档为主体骨架,结合仓库内 tabs.component.ts、tab.component.ts、tab-nav-bar.component.ts 等源码实现,完整梳理nz-tabs/nz-tab的全部 API、典型使用场景与底层运行机制。读完本文,你将能够熟练运用三类页签形态(line / card / editable-card)、自定义指示条、路由联动、懒加载渲染等高级能力,并理解其内部实现原理。
何时使用:Ant Design 的三级选项卡体系
Tabs 的核心价值是提供平级的区域将大块内容进行收纳和展现,保持界面整洁。Ant Design 依次提供了三级选项卡,分别用于不同的场景:
- 卡片式的页签:提供可关闭的样式,常用于容器顶部(对应
nzType="card"与nzType="editable-card"); - 标准线条式页签:用于容器内部的主功能切换,这是最常用的 Tabs(对应默认的
nzType="line"); - RadioButton:可作为更次级的页签来使用(属于 radio 组件,见 components/radio)。
从源码看,这三级形态通过 tabs.component.ts 的 host 绑定完成样式切换:nzType为card或editable-card时追加ant-tabs-card类,editable-card额外追加ant-tabs-editable与ant-tabs-editable-card类,同时nzTabPosition对应ant-tabs-top/bottom/left/right,nzSize对应ant-tabs-default/small/large,nzCentered对应ant-tabs-centered。
快速上手
使用前需先引入模块(各 demo 均以imports: [NzTabsModule]方式在独立组件中按需导入,也可在根模块统一引入 tabs.module.ts 导出的NzTabsModule)。最基础的双向绑定用法如下:
<nz-tabs [(nzSelectedIndex)]="selectedIndex"> <nz-tab nzTitle="Tab 1">Content of Tab Pane 1</nz-tab> <nz-tab nzTitle="Tab 2">Content of Tab Pane 2</nz-tab> <nz-tab nzTitle="Tab 3">Content of Tab Pane 3</nz-tab> </nz-tabs>import { Component, signal } from '@angular/core'; @Component({ selector: 'app-basic-tabs', imports: [NzTabsModule], template: `...` }) export class BasicTabsComponent { readonly selectedIndex = signal(0); }nz-tabs API 详解
nz-tabs是标签页容器,其全部输入输出参数如下表(与官方文档一致,并标注全局配置能力):
| 参数 | 说明 | 类型 | 默认值 | 全局配置 | 版本 |
|---|---|---|---|---|---|
[nzSelectedIndex] | 当前激活 tab 面板的序列号,可双向绑定 | number | - | ||
[nzAnimated] | 是否使用动画切换 Tabs,在nzTabPosition="top" \| "bottom"时有效 | boolean \| {inkBar:boolean, tabPane:boolean} | true, 当type="card"时为false | ✅ | |
[nzSize] | 大小,提供largedefault和small三种大小 | 'large' \| 'small' \| 'default' | 'default' | ✅ | |
[nzTabBarExtraContent] | tab bar 上额外的元素 | TemplateRef<void> | - | ||
[nzTabBarStyle] | tab bar 的样式对象 | object | - | ||
[nzTabPosition] | 页签位置,可选值有toprightbottomleft | 'top' \| 'right' \| 'bottom' \| 'left' | 'top' | ||
[nzType] | 页签的基本样式 | 'line' \| 'card' \| 'editable-card' | 'line' | ✅ | |
[nzTabBarGutter] | tabs 之间的间隙 | number | - | ✅ | |
[nzHideAll] | 是否隐藏所有 tab 内容 | boolean | false | ||
[nzLinkRouter] | 与 Angular 路由联动 | boolean | false | ||
[nzLinkExact] | 以严格匹配模式确定联动的路由 | boolean | true | ||
[nzCanDeactivate] | 决定一个 tab 是否可以被切换 | NzTabsCanDeactivateFn | - | ||
[nzCentered] | 标签居中展示 | boolean | false | ||
[nzDestroyInactiveTabPane] | 被隐藏时是否销毁 DOM 结构 | boolean | false | ||
[nzIndicator] | 自定义指示条宽度和对齐方式 | NzIndicator | - | 21.2.0 | |
(nzSelectedIndexChange) | 当前激活 tab 面板的序列号变更回调函数 | EventEmitter<number> | - | ||
(nzSelectChange) | 当前激活 tab 面板变更回调函数 | EventEmitter<{index: number,tab: NzTabComponent}> | - |
关键参数源码级解读
nzAnimated动画开关:源码将其建模为NzAnimatedInterface(见 interfaces.ts),包含inkBar(指示条动画)与tabPane(面板动画)两个开关;传入布尔值时两者同步生效。由 tabs.component.ts 的inkBarAnimated/tabPaneAnimated两个 getter 计算:指示条动画仅在nzType === 'line'时生效,这与文档"在top/bottom时有效"的说明一致——type="card"时无指示条,动画默认为false。
nzTabBarGutter页签间隙:在 tabs.component.ts 的模板中,通过[style.margin-inline-end.px]与[style.margin-bottom.px]分别作用于横向(top/bottom)与纵向(left/right)布局,实现 tab 之间的间距控制。
nzIndicator自定义指示条(21.2.0 新增):类型定义见 interfaces.ts:
export type NzIndicatorAlign = 'start' | 'end' | 'center'; export interface NzIndicator { size?: number | ((origin: number) => number); align: NzIndicatorAlign; }size既可以是固定像素值,也可以是接收原始尺寸并返回新尺寸的函数;align控制对齐方式。其底层实现在 tabs-ink-bar.directive.ts:指示条宽度取size(函数形式则传入当前激活 tab 的offsetWidth/offsetHeight作为origin计算),位置则由setIndicatorPosition依据align计算——start对齐 tab 起点、end对齐 tab 终点、center居中;RTL 场景下start与end会自动互换。demo/indicator.ts 展示了完整用法:
protected readonly indicator = computed<NzIndicator>(() => ({ size: origin => origin - 25, align: this.positionIndicator() }));nzSelectedIndex的双向绑定与事件流:nzSelectedIndexChange与nzSelectChange的触发集中在ngAfterContentChecked(tabs.component.ts):当indexToSelect与已选索引不一致时,通过createChangeEvent(tabs.component.ts)构造NzTabChangeEvent(含index与tab两个字段,见 interfaces.ts),先触发nzSelectChange,再在微任务中触发nzSelectedIndexChange,同时为非激活 tab 发出nzDeselect、为激活 tab 发出nzSelect。新增/删除 tab 时索引会自动 clamp 到合法范围(clampTabIndex)。
nzCanDeactivate切换守卫:类型为NzTabsCanDeactivateFn(见 interfaces.ts),签名(fromIndex: number, toIndex: number) => Observable<boolean> | Promise<boolean> | boolean。源码在 tabs.component.ts 中通过wrapIntoObservable统一包装为 Observable,并在setSelectedIndex(tabs.component.ts)中订阅结果:仅当返回true时才真正切换。clickNavItem(tabs.component.ts)显示点击时先发出nzClick,且路由联动时对链接点击不重复触发选中逻辑。仓库 demo/guard.ts 有完整守卫示例。
nzHideAll隐藏全部内容:为true时不渲染任何 tab 面板(见 tabs.component.ts 模板中的@if (!nzHideAll)),仅保留 tab 头;路由联动模式下当 URL 与任何 tab 链接都不匹配时也会自动置为true(updateRouterActive中this.nzHideAll = index === -1)。
nz-tabs[nzType="editable-card"]:可编辑卡片页签
当nzType="editable-card"时,nz-tabs额外支持添加与关闭能力:
| 参数 | 说明 | 类型 | 默认值 | 全局配置 |
|---|---|---|---|---|
[nzHideAdd] | 隐藏添加按钮 | boolean | false | |
[nzAddIcon] | 添加按钮图标 | string \| TemplateRef<void> | - | |
(nzAdd) | 点击添加按钮时的事件 | EventEmitter<> | - | |
(nzClose) | 点击删除按钮时的事件 | EventEmitter<{ index: number }> | - |
源码中addable与closable的判定见 tabs.component.ts:addable = nzType === 'editable-card' && !nzHideAdd,closable = nzType === 'editable-card';默认添加图标为'plus'(tabs.component.ts)。点击关闭按钮时onClose会先preventDefault与stopPropagation再发出nzClose,避免触发选中。仓库 demo/editable-card.ts 给出完整示例:
@Component({ selector: 'nz-demo-tabs-editable-card', imports: [NzTabsModule], template: ` <nz-tabs [(nzSelectedIndex)]="selectedIndex" nzType="editable-card" (nzAdd)="newTab()" (nzClose)="closeTab($event)"> @for (tab of tabs(); track tab) { <nz-tab nzClosable [nzTitle]="tab">Content of {{ tab }}</nz-tab> } </nz-tabs> ` }) export class NzDemoTabsEditableCardComponent { readonly tabs = signal(['Tab 1', 'Tab 2']); readonly selectedIndex = signal(0); closeTab({ index }: { index: number }): void { this.tabs.update(tabs => tabs.filter((_, i) => i !== index)); } newTab(): void { this.tabs.update(tabs => [...tabs, 'New Tab']); this.selectedIndex.set(this.tabs().length); } }注意:新增 tab 后需手动将nzSelectedIndex指向新 tab(如上this.selectedIndex.set(this.tabs().length)),否则选中态不会自动跳转。
nz-tab:单个标签页
nz-tab用于声明单个标签页,输入输出参数如下:
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
[nzTitle] | 选项卡头显示文字 | string \| TemplateRef<TabTemplateContext> | - |
[nzForceRender] | 被隐藏时是否渲染 DOM 结构 | boolean | false |
[nzDisabled] | 是否禁用 | boolean | - |
(nzClick) | 单击 title 的回调函数 | EventEmitter<void> | - |
(nzContextmenu) | 右键 title 的回调函数 | EventEmitter<MouseEvent> | - |
(nzSelect) | tab 被选中的回调函数 | EventEmitter<void> | - |
(nzDeselect) | tab 被取消选中的回调函数 | EventEmitter<void> | - |
面板渲染策略:懒加载、预渲染与销毁
nzForceRender、nzDestroyInactiveTabPane与nzHideAll共同决定了 tab 面板的 DOM 生命周期。模板渲染逻辑见 tabs.component.ts:
- 默认(两者均为 false):仅渲染"当前激活过的"面板(
nzSelectedIndex === $index || tab.hasBeenActive),即懒加载 + 激活后保留 DOM——首次选中才创建,切换后不销毁,保证再次切换回来时状态不丢失; nzForceRender为 true:面板从一开始就渲染,适用于需要预加载或内容初始化较重的场景;nzDestroyInactiveTabPane为 true:仅渲染当前激活面板,切走即销毁 DOM,再次切回重新创建(表单输入等内部状态会丢失)。
hasBeenActive标记在 tab.component.ts 的setActive中维护。nzTitle、nzDisabled、nzForceRender变化时会通过stateChanges通知容器重新检测(tab.component.ts)。
nz-tab[nzTitle] 的模版引用变量
当nzTitle为模板时,模板上下文暴露visible属性:表示 tab 是否在可见区域,为false时将会被渲染到下拉菜单中(tab 过多溢出时,超出可视区域的 tab 会收纳进"更多"下拉,见 tab-nav-operation.component.ts)。模板上下文类型为 interfaces.ts 中的TabTemplateContext { visible: boolean }。
在nz-tab[nzTitle]中使用:
<nz-tab [nzTitle]="titleTemplate"> ... <ng-template #titleTemplate let-visible="visible">...</ng-template> </nz-tab>在*nzTabLink中使用:
<nz-tab> <a *nzTabLink="let visible = visible" nz-tab-link [routerLink]="['.']">...</a> </nz-tab>从源码看,tab.component.ts 的labelgetter 优先取nzTitle,其次取nzTabLinkTemplateDirective.templateRef,因此标题文本、模板标题与链接标题三者互斥覆盖。
[nz-tab]:懒加载内容标记指令
[nz-tab]与ng-template一同使用,用于标记需要懒加载的 tab 内容,具体用法见 demo/lazy.ts:
<nz-tabs> <nz-tab nzTitle="Lazy Tab"> <ng-template nz-tab>Content loaded lazily</ng-template> </nz-tab> </nz-tabs>该指令本身极简(tab.directive.ts),selector 为[nz-tab],其作用仅是标记模板;容器通过@ContentChild(NzTabDirective, { read: TemplateRef })(tab.component.ts)读取模板引用,contentgetter 优先返回懒加载模板,否则返回组件默认内容模板(tab.component.ts)。模板注入的nz-tab-body(tab-body.component.ts)负责实际挂载面板。
ng-template[nzTabLink] > a[nz-tab-link]:与路由联动
路由联动可以让 tab 的切换和路由行为相一致。使用nzLinkRouter开启联动,并在 tab 内放置带nz-tab-link的链接:
<nz-tabs nzLinkRouter> <nz-tab> <a *nzTabLink nz-tab-link [routerLink]="['.']">Link</a> Default. </nz-tab> </nz-tabs>相关指令定义见 tab-link.directive.ts:ng-template[nzTabLink]提供模板上下文(nzTabLinkTemplateDirective),a[nz-tab-link]则捕获宿主元素与routerLink指令(NzTabLinkDirective,通过@ContentChild注入linkDirective)。
联动机制的核心在 tabs.component.ts:
setUpRouter在nzLinkRouter开启时监听Router的NavigationEnd事件与tabLinks.changes,订阅路由变化后调用updateRouterActive;updateRouterActive通过findShouldActiveTabIndex找到当前 URL 匹配的 tab,并自动调用setSelectedIndex切换;若没有任何匹配则设置nzHideAll = true(隐藏全部面板);nzLinkExact决定匹配模式:默认true时用paths: 'exact'严格匹配(URL 完全一致才算激活),设为false时用subset子路径匹配;isRouterLinkClickEvent(tabs.component.ts)判断点击是否落在链接元素内,若是则交由路由导航处理,避免 tab 选择与路由跳转互相冲突;- 若开启了
nzLinkRouter但未导入RouterModule,setUpRouter会抛出明确错误提示。
[nzTabBarExtraContent]:tab bar 附加内容
用于在 tab 栏两侧追加自定义内容。需要注意:*nzTabBarExtraContent比nz-tabs[nzTabBarExtraContent]具有更高的优先级(属性式写法会被指令式写法覆盖)。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
[nzTabBarExtraContent] | 附加内容的位置 | 'start' \| 'end' | 'end' |
指令实现见 tab-bar-extra-content.directive.ts,selector 为[nzTabBarExtraContent]:not(nz-tabs),通过position输入指定'start'(tab 栏左侧)或'end'(tab 栏右侧)。渲染时 tab-nav-bar.component.ts 分别在导航列表前、后输出ant-tabs-extra-content容器:startExtraContent优先于extraTemplate,endExtraContent优先于extraTemplate——这正是文档所述优先级关系的源码依据。属性式用法:
<nz-tabs [nzTabBarExtraContent]="extraTemplate"> ... </nz-tabs> <ng-template #extraTemplate>Extra Content</ng-template>指令式用法(可同时控制位置):
<nz-tabs> <ng-template nzTabBarExtraContent nzTabBarExtraContent="start">Left</ng-template> <ng-template nzTabBarExtraContent nzTabBarExtraContent="end">Right</ng-template> ... </nz-tabs>全局配置
上表标注 ✅ 的参数(nzType、nzSize、nzAnimated、nzTabBarGutter)支持通过 NG-ZORRO 全局配置服务统一设置,模块名为tabs(见 tabs.component.ts 的_nzModuleName: NzConfigKey = 'tabs',配合@WithConfig()装饰器使用)。例如在应用中统一将页签类型设为卡片式:
import { NzConfigService } from 'ng-zorro-antd/core/config'; const nzConfig = { tabs: { nzType: 'card' as const, nzSize: 'small' as const } }; // 通过 NzConfigService.setConfig(nzConfig) 或 NZ_CONFIG 令牌注入全局配置局部组件仍可通过输入属性覆盖全局配置。
源码级运行机制补充
指示条对齐与滚动:tab-nav-bar.component.ts 中的NzTabNavBarComponent负责整条 tab 导航栏的布局、滚动与指示条对齐。它通过NzResizeObserver监听容器尺寸变化、以 16ms 节流触发realign(重算滚动位置 + 对齐指示条),并通过setVisibleRange(tab-nav-bar.component.ts)计算可视区域:超出部分的 tab 进入hiddenItems,在"更多"下拉菜单中展示,同时设置pingLeft/pingRight/pingTop/pingBottom阴影提示可滚动方向。滚动过程对横向/纵向布局分别计算transformX/transformY并直接写入transform样式(setTransform),全程在runOutsideAngular中执行以避免频繁触发变更检测。
键盘可访问性:导航栏使用@angular/cdk/a11y的FocusKeyManager(tab-nav-bar.component.ts)实现方向键(左右/上下)在 tab 间移动焦点、Enter/Space激活选中(tab-nav-bar.component.ts),并支持 RTL 方向适配与循环(withWrap)。每个 tab 同时带有完整的role="tab"、aria-selected、aria-controls等 ARIA 属性(见 tabs.component.ts),内容面板为role="tabpanel"。
内容分组:nz-tabs通过NZ_TAB_SET注入令牌(tab.component.ts)与@ContentChildren(NzTabComponent, { descendants: true })收集全部 tab,再依据closestTabSet过滤出直属自身的 tab(subscribeToAllTabChanges,tabs.component.ts),因此支持嵌套 Tabs 而互不干扰。
更多示例
仓库 components/tabs/demo 提供了 13 个可直接运行的官方示例,覆盖本文所有能力:basic(基础)、card(卡片)、card-top(容器顶部卡片)、editable-card(可编辑卡片)、centered(居中)、disabled(禁用)、draggable(拖拽排序)、extra(附加内容)、guard(切换守卫)、icon(图标标题)、indicator(自定义指示条)、lazy(懒加载)、link-router(路由联动)、position(四个方位)、size(尺寸)、slide(滑动动画)。每个示例均含.ts与.md说明文件,可对照阅读快速落地。
- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
相关推荐
NG-ZORRO Radio 单选框组件完整实战指南:API 详解、单选组协同与源码级原理剖析
NG ZORRO Radio 单选框组件完整实战指南:API 详解、单选组协同与源码级原理剖析 导读 本文以 NG ZORRO https://link.git
UI组件前端ng-zorro-antd AutoComplete 组件完全指南:API 详解、交互原理与源码剖析
ng zorro antd AutoComplete 组件完全指南:API 详解、交互原理与源码剖析 导读 AutoComplete(自动完成)是 ng zor
UI组件前端ng-zorro-antd Popconfirm 弹出确认组件完全指南:API 详解与源码级原理剖析
ng zorro antd Popconfirm 弹出确认组件完全指南:API 详解与源码级原理剖析 Popconfirm(弹出确认框)是 ng zorro a
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考