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

如何基于配置生成带多级子页面的Angular侧边栏组件?

Angular 多级侧边栏菜单动态渲染解决方案

需要将给定的HTML侧边栏片段整合到Angular组件中,基于EmployeeLayoutSidebar配置实现多级子页面的动态渲染,此前尝试嵌套*ngFor和*ngIf未解决子菜单渲染问题。

第一步:修正配置拼写错误

原配置中存在icoon拼写错误,先修正为icon:

export const EmployeeLayoutSidebar = [
  {
    title: 'Startseite',
    icon: 'ni ni-tv-2',
    url: '#'
  },
  {
    title: 'Rechnungstool',
    icon: 'fas fa-money-bill-wave',
    link: '#',
  },
  {
    title: 'Formulare',
    icon: 'fas fa-file-alt',
    link: '#',
    subpages: [
      {
        title: 'Bewertungen',
        icon: 'caret',
        link: '#',
        subpages: [
          {
            title: 'Azubi',
            icon: '', // 修正原配置拼写错误:icoon → icon
            link: '#',
          }
        ],
      },
      {
        title: 'Bewerbung',
        icon: '',
        link: '#',
      }
    ],
  }
];

第二步:组件TS逻辑实现

在Angular组件中引入配置,管理菜单展开状态:

import { Component } from '@angular/core';
import { EmployeeLayoutSidebar } from './path-to-your-config'; // 替换为实际配置路径

@Component({
  selector: 'app-employee-sidebar',
  templateUrl: './employee-sidebar.component.html',
  styleUrls: ['./employee-sidebar.component.scss']
})
export class EmployeeSidebarComponent {
  sidebarItems = EmployeeLayoutSidebar;
  expandedMenus: { [key: string]: boolean } = {};

  // 切换菜单展开/折叠状态
  toggleMenu(title: string): void {
    this.expandedMenus[title] = !this.expandedMenus[title];
  }

  // 判断菜单是否展开
  isMenuExpanded(title: string): boolean {
    return this.expandedMenus[title] || false;
  }
}

第三步:组件HTML递归渲染

使用ng-template实现多级子菜单的递归渲染,保留原HTML所有样式类:

<ul class="flex flex-col pl-0 mb-0 list-none">
  <!-- 遍历一级菜单 -->
  <li class="mt-0.5 w-full" *ngFor="let item of sidebarItems">
    <!-- 无下级菜单的普通链接 -->
    <a 
      *ngIf="!item.subpages"
      [href]="item.url || item.link"
      class="ease-in-out text-sm leading-default py-2.7 my-0 mx-2 flex items-center whitespace-nowrap px-4 font-medium text-slate-500 shadow-none rounded-lg transition-colors dark:text-white dark:opacity-80"
    >
      <div *ngIf="item.icon" class="stroke-none flex h-6 w-8 items-center justify-center rounded-lg bg-center fill-current p-2.5 text-center text-black">
        <i class="text-sm leading-normal {{ item.icon }} text-slate-700/80 dark:text-white"></i>
      </div>
      <span class="ml-1 duration-300 opacity-100 pointer-events-none ease">{{ item.title }}</span>
    </a>

    <!-- 有下级菜单的折叠触发器 -->
    <a 
      *ngIf="item.subpages"
      href="javascript:;"
      class="ease-in-out text-sm leading-default py-2.7 my-0 mx-2 flex items-center whitespace-nowrap px-4 font-medium text-slate-500 shadow-none transition-colors after:ml-auto after:inline-block after:font-bold after:text-slate-800/50 after:antialiased after:transition-all after:duration-200 after:content-['\\f107'] dark:text-white dark:opacity-80 dark:after:text-white/50 dark:after:text-white"
      (click)="toggleMenu(item.title)"
      [attr.aria-expanded]="isMenuExpanded(item.title)"
      [attr.aria-controls]="item.title.toLowerCase().replace(' ', '')"
    >
      <div class="stroke-none flex h-6 w-8 items-center justify-center rounded-lg bg-center fill-current p-2.5 text-center text-black">
        <i class="text-sm leading-normal {{ item.icon }} text-slate-700/80 dark:text-white"></i>
      </div>
      <span class="ml-1 duration-300 opacity-100 pointer-events-none ease">{{ item.title }}</span>
    </a>

    <!-- 一级子菜单容器 -->
    <div 
      *ngIf="item.subpages"
      [id]="item.title.toLowerCase().replace(' ', '')"
      class="h-auto overflow-hidden transition-all duration-200 ease-in-out"
      [style.max-height]="isMenuExpanded(item.title) ? '999px' : '0'"
    >
      <ul class="flex flex-wrap mb-0 ml-6 list-none transition-all duration-200 ease-in-out">
        <ng-container *ngTemplateOutlet="submenuTemplate; context: { $implicit: item.subpages }"></ng-container>
      </ul>
    </div>
  </li>
</ul>

<!-- 递归子菜单模板,支持无限层级 -->
<ng-template #submenuTemplate let-items>
  <li class="w-full" *ngFor="let item of items">
    <!-- 下级子菜单触发器 -->
    <a 
      *ngIf="item.subpages"
      href="javascript:;"
      class="after:ease-in-out after:font-awesome-5-free ease-in-out py-2.7 ml-5.4 pl-4 leading-default text-sm relative my-0 mr-2 flex items-center whitespace-nowrap bg-transparent pr-4 font-medium text-slate-800/50 shadow-none transition-colors after:ml-auto after:inline-block after:font-bold after:text-slate-800/50 after:antialiased after:transition-all after:duration-200 after:content-['\\f107'] dark:text-white dark:opacity-60 dark:after:text-white/50 dark:after:text-white"
      (click)="toggleMenu(item.title)"
      [attr.aria-expanded]="isMenuExpanded(item.title)"
    >
      <span class="w-0 text-center transition-all duration-200 ease-in-out opacity-0 pointer-events-none"> {{ item.title.charAt(0) }} </span>
      <span class="transition-all duration-100 pointer-events-none ease"> {{ item.title }} <b *ngIf="item.icon === 'caret'" class="caret"></b></span>
    </a>

    <!-- 无下级的子菜单链接 -->
    <a 
      *ngIf="!item.subpages"
      [href]="item.link"
      class="ease-in-out py-2.7 ml-5.4 pl-4 text-sm relative my-0 mr-2 flex items-center whitespace-nowrap bg-transparent pr-4 font-medium text-slate-800/50 shadow-none transition-colors dark:text-white dark:opacity-60"
    >
      <span class="w-0 text-xs leading-tight text-center transition-all duration-200 ease-in-out opacity-0 pointer-events-none"> {{ item.title.charAt(0) }} </span>
      <span class="transition-all duration-100 pointer-events-none ease"> {{ item.title }} </span>
    </a>

    <!-- 渲染更深层级的子菜单 -->
    <div 
      *ngIf="item.subpages"
      class="h-auto overflow-hidden transition-all duration-200 ease-in-out"
      [style.max-height]="isMenuExpanded(item.title) ? '999px' : '0'"
    >
      <ul class="flex flex-col flex-wrap pl-0 mb-0 list-none transition-all duration-200 ease-in-out">
        <ng-container *ngTemplateOutlet="submenuTemplate; context: { $implicit: item.subpages }"></ng-container>
      </ul>
    </div>
  </li>
</ng-template>

关键说明

  • 递归模板submenuTemplate支持任意深度的子菜单渲染,适配配置中无限层级的subpages
  • 用菜单标题作为唯一标识管理展开状态,避免不同菜单间的状态冲突
  • 完全保留原HTML的样式类,确保视觉效果与原片段一致
  • 可根据Angular路由状态添加active_primary等激活类(可自行结合Router模块实现)

内容的提问来源于stack exchange,提问作者Elias

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:17:37