需求
为了满足菜单无限展开的需求,使用递归原理实现菜单的可以无限层级展开
菜单
基于antd菜单的基础上修改,代码如下:
<ul nz-menu nzMode="inline" [nzTheme]="theme ? 'dark' : 'light'" [nzInlineCollapsed]="isCollapsed">
<ng-container *ngFor="let menu of menus">
<ng-container *ngIf="menu.children && menu.children.length>0;else menufalse">
<li nz-submenu [nzTitle]="menu.text" nzIcon="appstore">
<ul>
<ng-container *ngFor="let item of menu.children">
<ng-container *ngIf="item.children && item.children.length>0;else MenuItem">
<app-submenu-item [nodes]="item.children" [subTitle]="item.text" [menuIcon]="item.icon">
</app-submenu-item>
</ng-container>
<ng-template #MenuItem>
<li nz-menu-item nzMatchRouter [nzPaddingLeft]="24">
<a [routerLink]="item.link">
<i nz-icon [nzType]="item.icon"></i>
<span>{{item.text}}</span>
</a>
</li>
</ng-template>
</ng-container>
</ul>
</li>
</ng-container>
<ng-template #menufalse>
<li nz-menu-item nzMatchRouter>
<a [routerLink]="menu.link">
<i nz-icon [nzType]="menu.icon"></i>
<span>{{menu.text}}</span>
</a>
</li>
</ng-template>
</ng-container>
</ul>
递归组件
创建递归组件submenu-item,代码如下:
<li nz-submenu [nzTitle]="subTitle">
<ul>
<ng-container *ngFor="let node of nodes">
<ng-container *ngIf="node.children && node.children.length>0;else MenuItem">
<app-submenu-item [nodes]="node.children" [subTitle]="node.text">
</app-submenu-item>
</ng-container>
<ng-template #MenuItem>
<li nz-menu-item nzMatchRouterExact routerLinkActive="active-link" [routerLinkActiveOptions]="{exact:
true}">
<a [routerLink]="node.link">
<i nz-icon [nzType]="node.icon" *ngIf="node.icon"></i>
<span>{{node.text}}</span>
</a>
</li>
</ng-template>
</ng-container>
</ul>
</li>
嵌套激活样式,配合路由自动激活
.ant-menu-item {
&.active-link {
background-color: #1890ff;
a {
color: #fff;
}
}
}
递归组件ts如下:
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-submenu-item',
templateUrl: './submenu-item.component.html',
styleUrls: ['./submenu-item.component.scss']
})
export class SubmenuItemComponent implements OnInit {
//#region data
@Input() nodes = [];
@Input() subTitle = '';
@Input() menuIcon = '';
//#endregion
constructor() { }
ngOnInit(): void {
}
}
读取本地菜单json数据,数据放于assets文件夹内,即项目默认静态文件放置地方
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-layout-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.scss']
})
export class LayoutMenuComponent implements OnInit {
//#region data
isCollapsed = false;
theme = true;
menus = [];
//#endregion
constructor(private readonly http: HttpClient) { }
ngOnInit(): void {
this.http.get('./assets/app-data.json').subscribe(data => {
this.menus = data as any;
});
}
}
菜单基本数据格式如下:
[{
"id": "01",
"text": "Dashboard",
"icon": "dashboard",
"children": [{
"id": "0101",
"text": "分析页",
"link": "/dashboard/analysis"
}
]
}]
ng-container 与 ng-template作用
ng-template:
<ng-template/>是一个 Angular 元素,用来渲染 HTML。 它永远不会直接显示出来。 事实上,在渲染视图之前,Angular 会把 及其内容替换为一个注释。 如果没有使用结构型指令,而仅仅把一些别的元素包装进 中,那些元素就是不可见的。
ng-container
Angular 的 <ng-container/> 是一个分组元素,但它不会污染样式或元素布局,因为 Angular 压根不会把它放进 DOM 中。类似vue的,微信的<block></block>标签避免添加额外的元素。