將 ng-alain 中的菜單替換成 ng-zorro-antd 的 nz-menu

首先,不得不說 @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)整

  1. 因?yàn)?ng-zorro-antd 的 nz-menu 折疊后的默認(rèn)寬度是 80px忘瓦,而 ng-alain 框架預(yù)留的寬度是64px搁廓,所以需要在 theme.less 中添加 @menu-collapsed-width: 64px; 修改 nz-menu 的寬度
  2. 菜單收縮后會顯示半截 title 的問題,因?yàn)?ng-zorro-antd 有一套自己的圖標(biāo)規(guī)范耕皮,因此按照它的規(guī)范使用<i class="anticon anticon-${type}"></i>境蜕,不建議在代碼中對<i class="{{ child1.icon }}"></i> 添加 margin-right
  3. 目前因?yàn)?angular 的問題,不限層級的寫法還存在問題凌停,因此默認(rèn)寫死最多展示三級粱年,具體情況代碼中我寫的已經(jīng)很清楚了

六. 與 sidebar-nav 的API 不兼容的問題

  1. 不支持 tooltip 和 badge,因?yàn)?ng-zorro-antd 并沒有此功能罚拟,所以我沒有花額外的時(shí)間去整合它台诗,若你需要它,你只需稍微改動 sidebar-menu.component.html 即可
  2. 關(guān)于 externalLink 和 target 赐俗,我并不清楚應(yīng)用場景拉队,所以也未作深究,你可以修改 sidebar-menu.component.ts 中的 onSelect 方法去實(shí)現(xiàn)它
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阻逮,一起剝皮案震驚了整個(gè)濱河市粱快,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叔扼,老刑警劉巖事哭,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓜富,居然都是意外死亡鳍咱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門食呻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來流炕,“玉大人,你說我怎么就攤上這事仅胞∶勘伲” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵干旧,是天一觀的道長渠欺。 經(jīng)常有香客問我,道長椎眯,這世上最難降的妖魔是什么挠将? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任胳岂,我火速辦了婚禮,結(jié)果婚禮上舔稀,老公的妹妹穿的比我還像新娘乳丰。我一直安慰自己,他們只是感情好内贮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布产园。 她就那樣靜靜地躺著,像睡著了一般夜郁。 火紅的嫁衣襯著肌膚如雪什燕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天竞端,我揣著相機(jī)與錄音屎即,去河邊找鬼。 笑死事富,一個(gè)胖子當(dāng)著我的面吹牛技俐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赵颅,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼虽另,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饺谬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤谣拣,失蹤者是張志新(化名)和其女友劉穎募寨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體森缠,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拔鹰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贵涵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片列肢。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宾茂,靈堂內(nèi)的尸體忽然破棺而出瓷马,到底是詐尸還是另有隱情,我是刑警寧澤跨晴,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布欧聘,位于F島的核電站,受9級特大地震影響端盆,放射性物質(zhì)發(fā)生泄漏怀骤。R本人自食惡果不足惜费封,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蒋伦。 院中可真熱鬧弓摘,春花似錦代箭、人聲如沸硼瓣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爷抓。三九已至势决,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蓝撇,已是汗流浹背果复。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渤昌,地道東北人虽抄。 一個(gè)月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像独柑,于是被迫代替她去往敵國和親迈窟。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容