【问题标题】:Angular Material sidenav directive does not work in app.componentAngular Material sidenav 指令在 app.component 中不起作用
【发布时间】:2019-03-26 13:51:30
【问题描述】:

我有一个带有 Material sidenav 的应用程序,sidenav 显示了一个带有链接的列表。其中一个链接有一个指令,如果用户权限不符合预期的权限,则隐藏它。如果我在标题组件中使用列表,一切正常,但在我的应用程序组件中,该指令在应用程序重新加载之前不起作用。我该如何解决?

app.component.ts

import { MatSidenav } from "@angular/material";
import { ViewChild, ViewEncapsulation, OnChanges } from "@angular/core";
import { Component, AfterViewInit, HostBinding, OnInit } from "@angular/core";
import {
  Router,
  NavigationStart,
  NavigationEnd,
  NavigationCancel
} from "@angular/router";
import { OverlayContainer } from "@angular/cdk/overlay";
import { CustomizeService } from "./services/shared-services/customize.service";
import { FocusMonitor } from "@angular/cdk/a11y";
import { SidenavService } from "./services/layout-services/sidenav.service";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements AfterViewInit, OnInit {
  loading;
  customTheme;
  // theme: string;
  @HostBinding("class") componentCssClass;
  @ViewChild("sidenav") public sidenav: MatSidenav;

  constructor(
    private router: Router,
    private sidenavService: SidenavService,
    private focusMonitor: FocusMonitor,
    public overlayContainer: OverlayContainer,
    private customizeService: CustomizeService
  ) {
    this.loading = true;
  }

  ngOnInit() {
    this.getTheme();
    this.sidenavService.setSidenav(this.sidenav);
  }

  ngAfterViewInit() {
    this.focusMonitor.stopMonitoring(document.getElementById("clienti"));
    this.router.events.subscribe(event => {
      if (event instanceof NavigationStart) {
        this.loading = true;
      } else if (
        event instanceof NavigationEnd ||
        event instanceof NavigationCancel
      ) {
        this.loading = false;
      }
    });
  }

  getTheme() {
    this.customizeService.getTheme().subscribe(theme => {
      this.overlayContainer.getContainerElement().classList.add(theme);
      this.componentCssClass = theme || this.getLogoFromLocalStorage();
    });
    // this.sendTheme(theme);
  }

  getLogoFromLocalStorage() {
    return localStorage.getItem("customStyle");
  }

  closeSidenav() {
    this.sidenavService.close();
  }
}

app.component.html

<div color="primary">

  <mat-sidenav-container class="example-container" (backdropClick)="closeSidenav()">
    <mat-sidenav #sidenav (keydown.escape)="closeSidenav()" disableClose>
      <mat-list>
        <mat-list-item (click)="closeSidenav()"><button id="clienti" mat-button [routerLink]="['/clienti']"
            routerLinkActive="mat-accent">Clienti</button></mat-list-item>
        <mat-list-item (click)="closeSidenav()"><button mat-button [routerLink]="['/utenti']" routerLinkActive="mat-accent">Utenti</button></mat-list-item>
        <!-- <mat-list-item (click)="closeSidenav()"><butto [routerLink]="['/matricole']" routerLinkActive="mat-accent">Matricole</mat-list-item> -->
        <mat-list-item (click)="closeSidenav()"><button mat-button [routerLink]="['/sks']" routerLinkActive="mat-accent">Sks</button></mat-list-item>
        <mat-list-item (click)="closeSidenav()" [appCheckPermissions]="9">
          <button mat-button [routerLink]="['/packs']" routerLinkActive="mat-accent">Pacchetti</button>
        </mat-list-item>
        <mat-list-item (click)="closeSidenav()"><button mat-button [routerLink]="['/pc']" routerLinkActive="mat-accent">Pc</button></mat-list-item>
        <mat-list-item (click)="closeSidenav()"><button mat-button [routerLink]="['/rinnovi']" routerLinkActive="mat-accent">Rinnovi</button></mat-list-item>
      </mat-list>
    </mat-sidenav>

    <mat-sidenav-content>
      <app-header></app-header>
      <div class="main" color="primary">
        <div [hidden]="!loading" class="loader">
          <app-progress-spinner></app-progress-spinner>
        </div>
        <div [hidden]="loading" class="router-output">
          <router-outlet></router-outlet>
        </div>
      </div>
      <app-footer></app-footer>
    </mat-sidenav-content>
  </mat-sidenav-container>

</div>

check-permission.directive.ts

import { Directive, ElementRef, Renderer2, HostListener, HostBinding, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
import { DataService } from '../services/shared-services/data.service';

@Directive({
  selector: '[appCheckPermissions]'
})
export class CheckPermissionsDirective {

  permissions: number[] = [];

  constructor(
    private data: DataService,
    private element: ElementRef,
    private renderer: Renderer2
  ) {
    this.getPerms();
  }

  @Input() set appCheckPermissions(perm: number) {
    if (this.permissions.includes(perm)) {
      // console.log('ok');
    } else {
      // console.log('non autorizzato');
      this.renderer.setStyle(this.element.nativeElement, 'display', 'none');
    }
  }

  getPerms() {
    this.data.getPermissionsFromToken().subscribe(permsArr => {
      this.permissions = permsArr;
    });
  }
}

编辑:permissions和_perm的日志

刷新前登录:

刷新后:

编辑 2

感谢 Sunil Singh 帮助我找到问题:当我注销然后再次登录时,permissions 数据不可用,需要 appCheckPermission 指令的 app.component 没有重新初始化,所以它在浏览器刷新之前获取旧的权限数据。

解决方法是基于服务的,我不知道如何使用指令进行修复(Sunil Singh 方式不起作用)。于是我写了一个permissionService:

permissions = [];

constructor(private data: DataService) {
  this.getPerms();
}

getPerms() {
  this.data.getPermissionsFromToken().subscribe(permsArr => {
    this.permissions = permsArr;
  });
}

isPacksManagerEnable() {
  return this.permissions.includes(9);
}

getPerms() 方法在菜单按钮单击时触发,然后在app.component 中注入服务并在模板上调用isPacksManagerEnable():

<mat-list-item (click)="closeSidenav()" *ngIf="permsService.isPacksManagerEnable()">
  <button mat-button [routerLink]="['/packs']" routerLinkActive="mat-accent">Pacchetti</button>
</mat-list-item>

希望对遇到同样问题的人有所帮助

【问题讨论】:

    标签: javascript angular typescript angular-material angular-directive


    【解决方案1】:

    问题

    您对指令中的permissions 和appCheckPermissions 有疑问。您正在检查当时不可用的异步数据permissions 的权限。加载 permissions 后,您不会触发负责隐藏和显示链接的设置器 appCheckPermissions。

    修复

    它有简单的修复,你需要确保每当permissions发生变化时,它应该触发权限检查。

    修改版

    check-permission.directive.ts

    import { Directive, ElementRef, Renderer2, HostListener, HostBinding, Input, OnInit, TemplateRef, ViewContainerRef, AfterViewInit } from '@angular/core';
    import { DataService } from '../services/shared-services/data.service';
    
    @Directive({
      selector: '[appCheckPermissions]'
    })
    export class CheckPermissionsDirective implements AfterViewInit {
    
      permissions: number[] = [];
    
      constructor(
        private data: DataService,
        private element: ElementRef,
        private renderer: Renderer2
      ) {
        //this.getPerms();
      }
    
      private _perm;    
    
      @Input() set appCheckPermissions(perm: number) {
         this._perm = perm;
         this.doPermissionCheck();
      }
    
      doPermissionCheck(){
       
        if (this.permissions.includes(this._perm)) {
          this.renderer.setStyle(this.element.nativeElement, 'display', 'block');
          // console.log('ok');
        } else {
          // console.log('non autorizzato');
          this.renderer.setStyle(this.element.nativeElement, 'display', 'none');
        }
    
      }
       ngAfterViewInit() {
        this.data.getPermissionsFromToken().subscribe(permsArr => {
          this.permissions = permsArr;
          this.doPermissionCheck();
        });
      }
    }
    

    注意:代码直接用stackoverflow editor 编写,因此可能存在拼写错误或语法错误。请自行修复。

    【讨论】:

    • 感谢您的回答,但在重新加载之前,这在 app.component 中还不起作用。在标题中工作正常。我需要一些东西来触发重新加载
    • reload 是什么意思?你刷新页面了吗?
    • 当我刷新页面或从控制台重新加载应用程序时,一切正常
    • 你能不能把控制台放在doPermissionCheck()里面,看看它被调用了多少次,this._perm和this.permissions的值是多少
    • 我已经编辑了我的问题。 PS:在菜单按钮单击时触发的服务工作正常
    猜你喜欢
    • 2018-11-21
    • 2016-10-26
    • 1970-01-01
    • 2016-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 1970-01-01
    相关资源
    最近更新 更多