首先,不得不說 @delon 是一個(gè)很不錯(cuò)的業(yè)務(wù)組件,它是 ng-alain 默認(rèn)使用的業(yè)務(wù)組件!
但是由于 delon 的菜單組件 sidebar-nav 的樣式是基于 css 控制的死讹,所以當(dāng)菜單折疊后,展示出來的子菜單曲梗,在某些情況操作下無法正常關(guān)閉赞警,是需要通過監(jiān)聽鼠標(biāo)的點(diǎn)擊事件來關(guān)閉的逛腿,因此我才決定使用 nz-menu去替換它,并且同時(shí)不破壞其它功能(如:路由守衛(wèi)仅颇、在 pad 下自動收縮菜單等)
一. 新建一個(gè)組件例如 sidebar-menu (目前是我將該組件建在 shared 目錄下)
$ cd src/app/shared/
$ mkdir components
$ cd components
$ ng g ng-zorro-antd:menu-inline-collapsed -p app --styleext='less' --name=sidebar-menu
二. 修改 sidebar-menu.component.html
<ul nz-menu [nzMode]="'inline'" nzTheme='light' [nzInlineCollapsed]="settings.layout.collapsed">
<ng-container *ngFor="let group of list">
<ng-template [ngIf]="group._hidden !== true">
<!-- 菜單分組 -->
<li nz-menu-group [hidden]="settings.layout.collapsed">
<span title>{{ group.text }}</span>
</li>
<!-- 寫法一:寫死
由于 angular 的問題 ngTemplateOutlet 生成的內(nèi)容現(xiàn)在 viewchildren 獲取不到
因此目前先寫死三層(一般超過三層就不應(yīng)該放在菜單里面)
https://github.com/angular/angular/issues/20810
-->
<!-- 第一層 -->
<ng-container *ngFor="let child1 of group.children">
<ng-container *ngIf="child1._hidden !== true">
<ng-container *ngIf="child1._type !== 3">
<li nz-menu-item [nzSelected]="child1._open" (click)="onSelect(child1)">
<span title>
<i class="{{ child1.icon }}"></i>
<span>{{ child1.text }}</span>
</span>
</li>
</ng-container>
<ng-container *ngIf="child1._type === 3">
<li nz-submenu [nzOpen]="child1._open">
<span title>
<i class="{{ child1.icon }}"></i>
<span>{{ child1.text }}</span>
</span>
<ul>
<!-- 第二層 -->
<ng-container *ngFor="let child2 of child1.children">
<ng-container *ngIf="child2._hidden !== true">
<ng-container *ngIf="child2._type !== 3">
<li nz-menu-item [nzSelected]="child2._open" (click)="onSelect(child2)">{{ child2.text }}</li>
</ng-container>
<ng-container *ngIf="child2._type === 3">
<li nz-submenu [nzOpen]="child2._open">
<span title>
<i class="{{ child2.icon }}"></i>
<span>{{ child2.text }}</span>
</span>
<ul>
<!-- 第三層 -->
<ng-container *ngFor="let child3 of child2.children">
<li nz-menu-item *ngIf="child3._hidden !== true" [nzSelected]="child3._open" (click)="onSelect(child3)">{{ child3.text }}</li>
</ng-container>
</ul>
</li>
</ng-container>
</ng-container>
</ng-container>
</ul>
</li>
</ng-container>
</ng-container>
</ng-container>
<!-- 寫法二 -->
<!-- 循環(huán)嵌套層 (需等待bug修復(fù) https://github.com/NG-ZORRO/ng-zorro-antd/issues/1326 ) -->
<!-- <ng-container *ngFor="let child of group.children">
<ng-container *ngTemplateOutlet="sidebarMenuTemplate; context: { $implicit: child }"></ng-container>
</ng-container> -->
</ng-template>
</ng-container>
<!-- 寫法二 -->
<!-- 菜單嵌套模板 -->
<!-- <ng-template #sidebarMenuTemplate let-navmenu>
<ng-container *ngIf="navmenu._hidden !== true">
<ng-container *ngIf="navmenu._type !== 3">
<li nz-menu-item [nzSelected]="navmenu._open" (click)="onSelect(navmenu)">
<ng-container *ngIf="navmenu._depth <= 1">
<span title>
<i class="{{ navmenu.icon }}"></i>
<span>{{ navmenu.text }}</span>
</span>
</ng-container>
<ng-container *ngIf="navmenu._depth > 1">
{{ navmenu.text }}
</ng-container>
</li>
</ng-container>
<ng-container *ngIf="navmenu._type === 3">
<li nz-submenu [nzOpen]="navmenu._open">
<span title>
<i class="{{ navmenu.icon }}"></i>
<span>{{ navmenu.text }}</span>
</span>
<ul>
<ng-container *ngFor="let c of navmenu.children">
<ng-container *ngTemplateOutlet="sidebarMenuTemplate; context: { $implicit: c }"></ng-container>
</ng-container>
</ul>
</li>
</ng-container>
</ng-container>
</ng-template> -->
</ul>
寫法一是寫死的单默,只有三層
寫法二在樣式上存在沒有縮進(jìn)的問題,你可以根據(jù)菜單的深度(menu._depth
) 手動在 sidebarMenuTemplate 模板中加入縮進(jìn)樣式
三. 修改 sidebar-menu.component.ts
import {
Component,
OnInit,
OnDestroy,
ChangeDetectorRef,
Input,
Output,
EventEmitter,
} from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { ReuseTabService } from '@delon/abc';
import { MenuService, SettingsService, Menu } from '@delon/theme';
import { Nav } from '@delon/abc/src/sidebar-nav/interface';
@Component({
selector: 'app-sidebar-menu',
templateUrl: './sidebar-menu.component.html',
})
export class SidebarMenuComponent implements OnInit, OnDestroy {
list: Nav[] = [];
private menuChange$: Subscription;
private reuseChange$: Subscription;
@Input() autoCloseUnderPad = true;
@Output() select = new EventEmitter<Menu>();
constructor(
private menuSrv: MenuService,
private reuseSrv: ReuseTabService,
public settings: SettingsService,
private router: Router,
private cd: ChangeDetectorRef,
) {}
ngOnInit() {
this.menuSrv.openedByUrl(this.router.url);
this.menuChange$ = <any>this.menuSrv.change.subscribe(res => {
this.list = res;
this.cd.detectChanges();
});
this.reuseChange$ = <any>this.reuseSrv.change.subscribe(res => {
this.updateOpen();
this.cd.detectChanges();
});
this.installUnderPad();
}
updateOpen() {
const currentLink = this.router.url;
let imenu: Nav;
this.menuSrv.visit((i, p) => {
if (i.link === currentLink) {
imenu = i;
} else {
i._open = false;
}
});
while (imenu) {
imenu._open = true;
imenu = imenu.__parent;
}
this.cd.markForCheck();
}
onSelect(item: Nav) {
this.select.emit(item);
if (item._type === 1) {
this.router.navigateByUrl(item.link);
} else if (item._type === 2) {
// ......
}
}
ngOnDestroy(): void {
this.menuChange$.unsubscribe();
this.reuseChange$.unsubscribe();
if (this.route$) {
this.route$.unsubscribe();
}
}
// region: Under pad
private route$: Subscription;
private installUnderPad() {
if (!this.autoCloseUnderPad) return;
this.route$ = <any>this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(s => this.underPad());
this.underPad();
}
private underPad() {
if (window.innerWidth < 992 && !this.settings.layout.collapsed) {
this.settings.setLayout('collapsed', true);
}
}
// endregion
}
四. 組件替換
將 layout/default/sidebar/sidebar.component.html 中的<sidebar-nav class="d-block py-lg"></sidebar-nav>
替換成<app-sidebar-menu class="d-block py-lg"></app-sidebar-menu>
五. 樣式調(diào)整
- 因?yàn)?ng-zorro-antd 的 nz-menu 折疊后的默認(rèn)寬度是 80px忘瓦,而 ng-alain 框架預(yù)留的寬度是64px搁廓,所以需要在 theme.less 中添加
@menu-collapsed-width: 64px;
修改 nz-menu 的寬度 - 菜單收縮后會顯示半截 title 的問題,因?yàn)?ng-zorro-antd 有一套自己的圖標(biāo)規(guī)范耕皮,因此按照它的規(guī)范使用
<i class="anticon anticon-${type}"></i>
境蜕,不建議在代碼中對<i class="{{ child1.icon }}"></i>
添加margin-right
- 目前因?yàn)?angular 的問題,不限層級的寫法還存在問題凌停,因此默認(rèn)寫死最多展示三級粱年,具體情況代碼中我寫的已經(jīng)很清楚了
六. 與 sidebar-nav 的API 不兼容的問題
- 不支持 tooltip 和 badge,因?yàn)?ng-zorro-antd 并沒有此功能罚拟,所以我沒有花額外的時(shí)間去整合它台诗,若你需要它,你只需稍微改動
sidebar-menu.component.html
即可 - 關(guān)于 externalLink 和 target 赐俗,我并不清楚應(yīng)用場景拉队,所以也未作深究,你可以修改
sidebar-menu.component.ts
中的onSelect
方法去實(shí)現(xiàn)它