如何基于配置生成带多级子页面的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
相关产品推荐
相关产品推荐

