- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
本文围绕 ng-zorro-antd 菜单组件中vertical(垂直)模式展开,讲解该模式下子菜单以「弹出」形式呈现的行为特性、官方 Demo 的完整用法,以及弹出层在源码层面的实现原理。读完你将掌握垂直菜单的模板写法、全部 API 参数、子菜单弹出定位机制,以及它与 inline、horizontal 两种模式之间的切换关系。
一、什么是垂直菜单:子菜单以弹出形式呈现
在 ng-zorro-antd 的菜单组件中,nz-menu支持三种模式(类型定义见 menu.types.ts):
export type NzMenuModeType = 'vertical' | 'horizontal' | 'inline';vertical(垂直菜单):菜单项自上而下垂直排列,子菜单以弹出的形式(pop-up)展开,悬浮在菜单主体的右侧(或左侧,视 RTL 与定位而定);horizontal(水平菜单):菜单项水平排列,子菜单向下弹出;inline(内嵌菜单):子菜单以展开/收起的方式内嵌在菜单主体内部,占据文档流空间,不脱离父容器。
官方 Demo 对垂直菜单的描述非常凝练(见 vertical.md):
子菜单是弹出的形式。(Submenus open as pop-ups.)
这正是垂直模式与内嵌模式最本质的区别:垂直模式下子菜单并不挤占主体布局,而是通过浮层(overlay)覆盖在页面其他内容之上,适合收纳多级导航结构,常用于侧边栏导航场景。
nzMode的默认值就是'vertical'(见 menu.directive.ts 中@Input() nzMode: NzMenuModeType = 'vertical';),也就是说,不加任何模式配置时菜单即为垂直弹出式菜单。
二、官方 Demo 解读:一个完整的垂直菜单实现
官方垂直菜单示例位于 vertical.ts,完整代码如下:
import { Component } from '@angular/core'; import { NzMenuModule } from 'ng-zorro-antd/menu'; @Component({ selector: 'nz-demo-menu-vertical', imports: [NzMenuModule], template: ` <ul nz-menu nzMode="vertical"> <li nz-submenu nzTitle="Navigation One" nzIcon="mail"> <ul> <li nz-menu-group nzTitle="Item 1"> <ul> <li nz-menu-item>Option 1</li> <li nz-menu-item>Option 2</li> </ul> </li> <li nz-menu-group nzTitle="Item 2"> <ul> <li nz-menu-item>Option 3</li> <li nz-menu-item>Option 4</li> </ul> </li> </ul> </li> <li nz-submenu (nzOpenChange)="change($event)" nzTitle="Navigation Two" nzIcon="appstore"> <ul> <li nz-menu-item>Option 5</li> <li nz-menu-item>Option 6</li> <li nz-submenu nzTitle="Submenu"> <ul> <li nz-menu-item>Option 7</li> <li nz-menu-item>Option 8</li> </ul> </li> </ul> </li> <li nz-submenu nzTitle="Navigation Three" nzIcon="setting"> <ul> <li nz-menu-item>Option 9</li> <li nz-menu-item>Option 10</li> <li nz-menu-item>Option 11</li> </ul> </li> </ul> `, styles: ` [nz-menu] { width: 240px; } ` }) export class NzDemoMenuVerticalComponent { change(value: boolean): void { console.log(value); } }这段模板展示了垂直菜单的几大关键构成:
- 根菜单:
<ul nz-menu nzMode="vertical">,通过nzMode显式声明垂直模式; - 一级子菜单:
<li nz-submenu nzTitle="..." nzIcon="...">,nzTitle定义标题文字,nzIcon定义标题前的图标类型(如mail、appstore、setting,图标来自 ng-zorro-antd 的图标系统); - 分组:
<li nz-menu-group nzTitle="Item 1">,用于将同一子菜单下的菜单项分组收纳; - 菜单项:
<li nz-menu-item>,是最终的导航叶子节点; - 嵌套子菜单:Demo 中 "Navigation Two" 下还嵌套了二级
nz-submenu("Submenu"),说明垂直菜单支持多级弹出结构,每一级子菜单都各自以浮层形式弹出; - 展开回调:
(nzOpenChange)="change($event)",当子菜单展开/收起时向外抛出boolean值,示例中仅打印日志。
由于垂直模式下子菜单是浮层,并不参与父容器布局,Demo 通过样式把根菜单固定为width: 240px,以模拟真实侧边导航栏的宽度。
三、垂直菜单相关 API 速查
垂直菜单使用的是菜单组件通用 API,完整参数表见官方文档 index.zh-CN.md,这里摘取与垂直模式强相关的部分。
[nz-menu](根菜单)
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
[nzInlineCollapsed] | inline时菜单是否收起状态 | boolean | - |
[nzInlineIndent] | inline模式的菜单缩进宽度 | number | 24 |
[nzMode] | 菜单类型,支持垂直、水平、内嵌三种 | 'vertical' \| 'horizontal' \| 'inline' | 'vertical' |
[nzSelectable] | 是否允许选中 | boolean | true |
[nzTheme] | 主题颜色 | 'light' \| 'dark' | 'light' |
(nzClick) | 点击nz-menu-item输出属性 | EventEmitter<NzMenuItemComponent> |
[nz-menu-item](菜单项)
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
[nzDisabled] | 是否禁用 | boolean | false |
[nzSelected] | 是否被选中 | boolean | false |
[nzMatchRouter] | 是否根据routerLink自动设定nzSelected | boolean | false |
[nzMatchRouterExact] | 是否路由完整精确匹配 | boolean | false |
[nzDanger] | 展示错误状态样式 | boolean | false |
[nz-submenu](子菜单,垂直弹出交互的核心)
nz-submenu的标题有三种写法,任选其一即可:
<li nz-submenu nzTitle="SubTitle" nzIcon="appstore"></li> <li nz-submenu> <span title> <nz-icon nzType="appstore" /> <span>SubTitle</span> </span> </li> <li nz-submenu [nzTitle]="titleTpl"></li> <ng-template #titleTpl> <nz-icon nzType="appstore" /> <span>SubTitle</span> </ng-template>| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
[nzPlacement] | 菜单弹出位置 | 'bottomLeft' \| 'bottomCenter' \| 'bottomRight' \| 'topLeft' \| 'topCenter' \| 'topRight' | 'bottomLeft' |
[nzOpen] | 是否展开,可双向绑定 | boolean | false |
[nzDisabled] | 是否禁用 | boolean | false |
[nzTitle] | 标题内容 | string \| TemplateRef<void> | - |
[nzIcon] | 标题中icon类型 | string | - |
[nzMenuClassName] | 自定义子菜单容器类名 | string | - |
[nzTriggerSubMenuAction] | SubMenu 展开/关闭的触发行为 | 'hover' \| 'click' | 'hover' |
(nzOpenChange) | 展开回调 | EventEmitter<boolean> | - |
[nz-menu-group](菜单分组)
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
[nzTitle] | 标题内容 | string \| TemplateRef<void> | - |
分组标题同样支持字符串、<span title>投影和TemplateRef三种写法。另外还有nz-menu-divider(菜单项分割线),官方说明为「只用在弹出菜单内」,恰好适用于垂直模式的浮层场景。
四、源码视角:垂直模式下子菜单如何「弹出」
「子菜单是弹出的形式」这句话背后,是一套基于 Angular CDK Overlay 的浮层实现。核心逻辑位于 submenu.component.ts。
1. inline 与 non-inline 的分叉渲染
NzSubMenuComponent的模板中,根据mode选择完全不同的子菜单渲染方式:
mode === 'inline':使用nz-submenu-inline-child,子菜单内嵌在父级文档流中,靠nzOpen控制展开/收起(见 submenu-inline-child.component.ts);- 其他模式(含
vertical):走@else分支,使用cdkConnectedOverlay创建独立浮层,只有nzOpen为true时才展示。
2. 垂直模式的弹出位置列表
源码中定义了垂直模式专用的定位列表(见 submenu.component.ts):
const listOfVerticalPositions = [ POSITION_MAP.rightTop, POSITION_MAP.right, POSITION_MAP.rightBottom, POSITION_MAP.leftTop, POSITION_MAP.left, POSITION_MAP.leftBottom ];这说明垂直模式的子菜单浮层默认优先贴附在触发项的右侧(rightTop对齐顶边、right垂直居中、rightBottom对齐底边),空间不足时依次回退到左侧。submenu.component.ts中的onPositionChange会依据实际落位将position置为'right'或'left',用于给浮层添加ant-menu-submenu-placement-right/-placement-left样式类。
3. 触发方式与联动
- 默认
nzTriggerSubMenuAction = 'hover',即鼠标悬停展开;可切换为'click'点击展开; nzSubmenuService.isCurrentSubMenuOpen$状态变化会同步nzOpen并向外触发(nzOpenChange)事件——这正是 Demo 中(nzOpenChange)="change($event)"能拿到boolean值的原因;- 浮层的
overlayOutsideClick与detach事件会把鼠标状态重置为未激活,实现点击外部区域收起子菜单; - 禁用态(
nzDisabled)下,submenu-non-inline-child.component.ts 中的setMouseState会忽略 hover 触发,禁止浮层展开。
4. 浮层容器与动画
弹出来的子菜单由NzSubmenuNoneInlineChildComponent渲染(见 submenu-non-inline-child.component.ts),它负责生成浮层容器的样式类:ant-menu-submenu ant-menu-submenu-popup ant-menu-light ant-menu-submenu-placement-right,并根据模式注入动画——垂直模式使用ant-zoom-big缩放动画,水平模式则使用slide-up动画。RTL 环境下还会附加ant-menu-submenu-rtl类。
五、垂直模式与 inline 模式的切换、收起联动
在 menu.directive.ts 中,根菜单通过inlineCollapsed$与mode$两个BehaviorSubject的合并流计算「实际生效模式」:
combineLatest([this.inlineCollapsed$, this.mode$]) .subscribe(([inlineCollapsed, mode]) => { this.actualMode = inlineCollapsed ? 'vertical' : mode; this.nzMenuService.setMode(this.actualMode); this.cdr.markForCheck(); });从源码可以推断出两点与垂直模式直接相关的行为:
inline菜单收起后自动退化为vertical:当nzMode="inline"且nzInlineCollapsed为true时,actualMode变为'vertical',子菜单重新以弹出形式呈现——这是侧边栏折叠(sider collapse)场景能正常工作的底层保证,对应官方 sider-current.md 与 inline-collapsed.md 两个 Demo;- 模式切换时收起所有子菜单:
ngOnChanges检测到nzMode变化时,会遍历所有子菜单调用setOpenStateWithoutDebounce(false),避免切换模式后遗留展开状态。
nzInlineIndent(默认24)与层级服务配合,为 inline 模式计算每级缩进;而垂直模式因为是浮层,不参与缩进计算(对应逻辑在 submenu.component.ts 的inlinePaddingLeft = mode === 'inline' ? this.level * inlineIndent : null)。
六、选中态与路由联动(垂直导航常见诉求)
垂直菜单多用于侧边导航,通常需要与路由联动自动高亮。nz-menu-item支持nzMatchRouter与nzMatchRouterExact两个开关,底层实现在 menu-item.component.ts:
- 组件监听
NavigationEnd路由事件,配合router.isActive判断当前 URL 是否命中菜单项内的RouterLink; nzMatchRouterExact为true时按paths: 'exact'精确匹配,否则按'subset'子路径匹配;- 命中后通过
setSelectedState(true)置为选中,同时子菜单组件会聚合其内部nz-menu-item的selected$流,自动把所属nz-submenu也标记为isSelected(高亮子菜单标题)。
点击行为方面,nz-menu-item的clickMenuItem会先禁用拦截,再依次通知MenuService的descendantMenuItemClick$与childMenuItemClick$;根菜单订阅前者后,若nzSelectable为true且未启用路由匹配,则清空其他项的选中态,只保留当前项,并向外触发(nzClick)。这套链路保证了垂直菜单多级结构下的选中互斥。
七、更多实战组合
垂直菜单常与其他能力组合使用,仓库中提供了可直接参考的配套 Demo:
- 主题切换:theme.md 演示
nzTheme="dark"暗色侧边栏;主题通过MenuService.theme$下发到每个子菜单浮层,浮层容器会相应添加ant-menu-dark类; - 模式动态切换:switch-mode.md 演示在三种模式间动态切换
nzMode; - 递归菜单:recursive.md 演示用组件递归渲染无限层级菜单,垂直弹出结构天然支持多级嵌套;
- 侧边栏场景:sider-current.md 将垂直菜单与 Layout 的 Sider 组合,实现完整的后台导航骨架。
结语
垂直菜单是 ng-zorro-antd 三种菜单模式中唯一「子菜单以弹出形式呈现」的侧边导航方案(该描述源自官方 Demo vertical.md)。通过nzMode="vertical"一行配置即可启用,配合nz-submenu、nz-menu-group、nz-menu-item组织多级结构,底层由 CDK Overlay 浮层、right/left双向定位和 hover 触发机制共同支撑。理解「弹出」背后的渲染分叉与定位逻辑,有助于你在侧边栏、后台管理系统中精准掌控导航交互细节。
- UI组件
- 前端
【免费下载链接】ng-zorro-antd
Angular UI Component Library based on Ant Design
相关推荐
Ant Design 垂直菜单(Vertical Menu)实战指南:弹出式子菜单的布局、交互与源码原理
Ant Design 垂直菜单(Vertical Menu)实战指南:弹出式子菜单的布局、交互与源码原理 垂直菜单是 Ant Design 中 mode="ve
UI组件前端设计系统ng-zorro-antd 右键菜单实战:用 NzContextMenuService 与 nz-dropdown-menu 构建 Context Menu
ng zorro antd 右键菜单实战:用 NzContextMenuService 与 nz dropdown menu 构建 Context Menu n
UI组件前端Ant Design Menu 内联模式(inline)完全指南:垂直菜单与内嵌子菜单的实战解析
Ant Design Menu 内联模式(inline)完全指南:垂直菜单与内嵌子菜单的实战解析 导读 在 Ant Design 的 Menu 组件中, mod
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考