news 2026/9/27 21:10:47

ng-zorro-antd 垂直菜单(Vertical Menu)实战指南:子菜单弹出式交互的原理与用法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ng-zorro-antd 垂直菜单(Vertical Menu)实战指南:子菜单弹出式交互的原理与用法
  • UI组件
  • 前端

【免费下载链接】ng-zorro-antd

Angular UI Component Library based on Ant Design

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载

本文围绕 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); } }

这段模板展示了垂直菜单的几大关键构成:

  1. 根菜单:<ul nz-menu nzMode="vertical">,通过nzMode显式声明垂直模式;
  2. 一级子菜单:<li nz-submenu nzTitle="..." nzIcon="...">,nzTitle定义标题文字,nzIcon定义标题前的图标类型(如mail、appstore、setting,图标来自 ng-zorro-antd 的图标系统);
  3. 分组:<li nz-menu-group nzTitle="Item 1">,用于将同一子菜单下的菜单项分组收纳;
  4. 菜单项:<li nz-menu-item>,是最终的导航叶子节点;
  5. 嵌套子菜单:Demo 中 "Navigation Two" 下还嵌套了二级nz-submenu("Submenu"),说明垂直菜单支持多级弹出结构,每一级子菜单都各自以浮层形式弹出;
  6. 展开回调:(nzOpenChange)="change($event)",当子菜单展开/收起时向外抛出boolean值,示例中仅打印日志。

由于垂直模式下子菜单是浮层,并不参与父容器布局,Demo 通过样式把根菜单固定为width: 240px,以模拟真实侧边导航栏的宽度。

三、垂直菜单相关 API 速查

垂直菜单使用的是菜单组件通用 API,完整参数表见官方文档 index.zh-CN.md,这里摘取与垂直模式强相关的部分。

[nz-menu](根菜单)

参数说明类型默认值
[nzInlineCollapsed]inline时菜单是否收起状态boolean-
[nzInlineIndent]inline模式的菜单缩进宽度number24
[nzMode]菜单类型,支持垂直、水平、内嵌三种'vertical' \| 'horizontal' \| 'inline''vertical'
[nzSelectable]是否允许选中booleantrue
[nzTheme]主题颜色'light' \| 'dark''light'
(nzClick)点击nz-menu-item输出属性EventEmitter<NzMenuItemComponent>

[nz-menu-item](菜单项)

参数说明类型默认值
[nzDisabled]是否禁用booleanfalse
[nzSelected]是否被选中booleanfalse
[nzMatchRouter]是否根据routerLink自动设定nzSelectedbooleanfalse
[nzMatchRouterExact]是否路由完整精确匹配booleanfalse
[nzDanger]展示错误状态样式booleanfalse

[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]是否展开,可双向绑定booleanfalse
[nzDisabled]是否禁用booleanfalse
[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(); });

从源码可以推断出两点与垂直模式直接相关的行为:

  1. inline菜单收起后自动退化为vertical:当nzMode="inline"且nzInlineCollapsed为true时,actualMode变为'vertical',子菜单重新以弹出形式呈现——这是侧边栏折叠(sider collapse)场景能正常工作的底层保证,对应官方 sider-current.md 与 inline-collapsed.md 两个 Demo;
  2. 模式切换时收起所有子菜单: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

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载
上一篇:Sliver 客户端 crack 命令组深入解析:GPU 分布式口令破解的完整工作流
下一篇:海尔智能家居集成Haier的API接口与认证机制深入分析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 21:10:14

张家界市建设网站选哪家?备案避坑与落地实操指南

张家界市建设网站选哪家?备案避坑与落地实操指南 做网站最让人头大的,往往不是代码怎么写,而是 备案流程一头雾水 。很多张家界本地的老板在找 张家界市建设网站哪家好…

作者头像 李华
网站建设 2026/9/27 21:10:07

浙江网站建设品牌设计报价全解析:避坑指南与费用明细

浙江网站建设品牌设计报价全解析:避坑指南与费用明细 自己不会代码想做网站,但看到“浙江网站建设品牌设计”这个词就头大?别慌。很多老板以为搞个官网就是填个表单、传张图,结果找了几家杭州的供应商,报价从五千到五万不等,功能描述还模棱两可。其实, 注意事项…

作者头像 李华
网站建设 2026/9/27 21:09:31

织梦商业网站内容管理系统2026最新

织梦商业系统实战案例:避开域名服务器坑 域名和服务器搞不懂,网站上线前夜最容易炸锅。 我见过太多客户,代码写完了,域名解析却指向了错误的IP,或者SSL证书没配对,导致Google Search Console里全是报错。 今天不聊虚的,直接上织梦(DedeCMS)商业版与主流开源CMS的…

作者头像 李华
网站建设 2026/9/27 21:09:24

南宁企业如何建网站避坑:域名服务器选型与性能优化实战

南宁企业如何建网站避坑:域名服务器选型与性能优化实战 域名买贵了,服务器选小了,SSL证书忘配了。这三个坑,80%的南宁中小企业在建站初期都会踩。很多人以为搞懂技术才是硬道理,其实 性能优化 和基础设施选型的匹配度,直接决定了你的网站是“秒开”还是“加载转圈”,更是决定客户留不留得住的关键。…

作者头像 李华
网站建设 2026/9/27 21:08:20

搜索引擎实训心得体会图解步骤与备案避坑全记录

搜索引擎实训心得体会图解步骤与备案避坑全记录 刚拿到ICP备案回执单时,我盯着那串验证码和“初审通过”四个字,心里并没有多少喜悦,反而是一头雾水。备案流程到底走到哪一步了?域名解析什么时候生效?SSL证书又要怎么配?这种对未知流程的恐惧感,比写代码报错更让人焦虑。…

作者头像 李华
网站建设 2026/9/27 21:08:20

论坛做视频网站有哪些完整流程避坑指南

论坛做视频网站有哪些完整流程避坑指南 模板网站太丑不够用?很多站长接手旧站,看着那些僵硬的卡片式布局,心里直打鼓:这玩意儿发个帖子还行,真要搞视频专区,用户刷起来累得慌,跳出率高得吓人。别急着删库重装,也别盲目堆砌花哨特效。…

作者头像 李华