美文网首页
将 ng-alain 中的菜单替换成 ng-zorro-antd

将 ng-alain 中的菜单替换成 ng-zorro-antd

作者: Tucke | 来源:发表于2018-07-11 14:37 被阅读626次

    首先,不得不说 @delon 是一个很不错的业务组件,它是 ng-alain 默认使用的业务组件!

    但是由于 delon 的菜单组件 sidebar-nav 的样式是基于 css 控制的,所以当菜单折叠后,展示出来的子菜单,在某些情况操作下无法正常关闭,是需要通过监听鼠标的点击事件来关闭的,因此我才决定使用 nz-menu去替换它,并且同时不破坏其它功能(如:路由守卫、在 pad 下自动收缩菜单等)


    一. 新建一个组件例如 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 生成的内容现在 viewchildren 获取不到
            因此目前先写死三层(一般超过三层就不应该放在菜单里面)
            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>
    
          <!-- 写法二 -->
          <!-- 循环嵌套层 (需等待bug修复 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>
    

    写法一是写死的,只有三层
    写法二在样式上存在没有缩进的问题,你可以根据菜单的深度( menu._depth ) 手动在 sidebarMenuTemplate 模板中加入缩进样式

    三. 修改 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>

    五. 样式调整

    1. 因为 ng-zorro-antd 的 nz-menu 折叠后的默认宽度是 80px,而 ng-alain 框架预留的宽度是64px,所以需要在 theme.less 中添加 @menu-collapsed-width: 64px; 修改 nz-menu 的宽度
    2. 菜单收缩后会显示半截 title 的问题,因为 ng-zorro-antd 有一套自己的图标规范,因此按照它的规范使用<i class="anticon anticon-${type}"></i>,不建议在代码中对<i class="{{ child1.icon }}"></i> 添加 margin-right
    3. 目前因为 angular 的问题,不限层级的写法还存在问题,因此默认写死最多展示三级,具体情况代码中我写的已经很清楚了

    六. 与 sidebar-nav 的API 不兼容的问题

    1. 不支持 tooltip 和 badge,因为 ng-zorro-antd 并没有此功能,所以我没有花额外的时间去整合它,若你需要它,你只需稍微改动 sidebar-menu.component.html 即可
    2. 关于 externalLink 和 target ,我并不清楚应用场景,所以也未作深究,你可以修改 sidebar-menu.component.ts 中的 onSelect 方法去实现它

    相关文章

      网友评论

          本文标题:将 ng-alain 中的菜单替换成 ng-zorro-antd

          本文链接:https://www.haomeiwen.com/subject/umrhpftx.html