Angular用动态数据创建嵌套菜单 解决子菜单替换主菜单问题
问题根因
子菜单替换主菜单、无法嵌套展开是三个问题叠加导致的:
- 模板中
*ngFor未声明index变量,传入getSubCatalogueCategories的index是undefined,子项数据无法正确挂载到对应父级节点上 - 仅定义了一级菜单的
mat-menu实例,未给父级菜单项绑定子菜单触发器matMenuTriggerFor,Angular Material无法识别嵌套关系 - 误操作时将根菜单绑定的数组直接赋值为子项数据,导致一级菜单直接渲染子项,出现「替换主菜单」的表现
修复方案
1. 调整TS层逻辑
增加子项加载标记避免重复请求,初始化分类数据结构:
ngOnInit(): void { this.appService.getCatalogueCategories(1, { pos: '6196319a431f76d2acd3c27d' }).subscribe((res: any) => { // 初始化每个分类的子项容器、加载状态标记 this.catalogueCategories = res.map(item => ({ ...item, children: [], subLoaded: false })); }); } getSubCatalogueCategories(layer: number, parentId: string, index: number) { const currentCategory = this.catalogueCategories[index]; // 已加载过子项直接返回,避免重复请求 if (currentCategory.subLoaded) return; this.appService.getCatalogueCategories(layer, { pos: '6196319a431f76d2acd3c27d' }, parentId).subscribe((subCategories: any) => { currentCategory.children = subCategories.map(sub => ({ ...sub, children: [], subLoaded: false })); currentCategory.subLoaded = true; }); } // 叶子分类点击逻辑,根据业务需求自行实现 handleCategorySelect(category: any) { console.log('选中分类:', category); }
2. 重构模板,用递归模板实现无限层级嵌套
通过ng-template递归渲染菜单项,自动为每个父级项绑定独立的子菜单实例:
<!-- 主菜单触发按钮 --> <button mat-button [matMenuTriggerFor]="categories"> <span class="pl-1 text-gray-90">All Departments</span> </button> <!-- 一级根菜单 --> <mat-menu #categories="matMenu" yposition="below"> <ng-container *ngTemplateOutlet="menuItemTpl; context: { $implicit: catalogueCategories, layer: 1 }"></ng-container> </mat-menu> <!-- 递归菜单项模板,支持任意层级子菜单 --> <ng-template #menuItemTpl let-itemList let-layer="layer"> <ng-container *ngFor="let item of itemList; let i = index"> <!-- 有子项的父级菜单:绑定子菜单触发器,鼠标移入时预加载子项 --> <button mat-menu-item *ngIf="item.children?.length > 0; else leafItemTpl" [matMenuTriggerFor]="subMenu" (mouseenter)="getSubCatalogueCategories(layer + 1, item.id, i)" > {{item.name}} </button> <!-- 无下级的叶子菜单:绑定选中事件 --> <ng-template #leafItemTpl> <button mat-menu-item (click)="handleCategorySelect(item)"> {{item.name}} </button> </ng-template> <!-- 当前项对应的子菜单实例 --> <mat-menu #subMenu="matMenu"> <ng-container *ngTemplateOutlet="menuItemTpl; context: { $implicit: item.children, layer: layer + 1 }"></ng-container> </mat-menu> </ng-container> </ng-template>
注意事项
- 不要把
mat-menu标签嵌套写在其他mat-menu内部,Angular Material的菜单是全局弹出层,必须放在和根菜单平级的位置,通过模板引用绑定 - 用
mouseenter触发子项加载比click体验更好,用户鼠标悬停时就预加载数据,点开子菜单无延迟,需要点击触发的话把事件换成(click)即可 - 如果需要支持三级及以上子菜单,递归模板会自动渲染,不需要额外写重复代码
内容的提问来源于stack exchange,提问作者Amine Daly
相关产品推荐
相关产品推荐

