You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:03:21