简体   繁体   English

Angular Material sidenav指令在app.component中不起作用

[英]Angular Material sidenav directive does not work in app.component

I have an app with Material sidenav, the sidenav shows a list with links. 我有一个带有Material sidenav的应用程序,sidenav显示带有链接的列表。 one of these link has a directive that hide it if the user permission does not fit with the expected one. 这些链接之一具有一条指令,如果用户权限不符合预期,该指令会将其隐藏。 If I use the list inside my header component, all works fine, but inside my app component the directive does not works until app reload. 如果我在标头组件中使用列表,则一切正常,但在我的应用程序组件中,该指令在重新加载应用程序之前不起作用。 How can I fix? 我该如何解决?

app.component.ts 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 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 入住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;
    });
  }
}

EDIT : log of permissions and _perm 编辑permissions_perm日志

on login before refresh: 在刷新之前登录:

在此处输入图片说明

after refresh: 刷新后:

在此处输入图片说明

EDIT 2 编辑2

thanks to Sunil Singh for help me to found the issue: the permissions data are not available when I logout and then login again, the app.component that requires the appCheckPermission directive is not re-initialized, so it takes the old permissions data until browser refresh. 感谢苏尼尔·辛格的帮助我发现了问题: permissions ,当我注销,然后重新登录,数据不可用app.component需要的appCheckPermission指令未重新初始化,所以它需要的旧数据的权限,直到浏览器刷新。

The workaround is service-based, I don't know how to fix with directive (the Sunil Singh way does not works). 解决方法是基于服务的,我不知道如何使用指令进行修复(Sunil Singh方法不起作用)。 So I write a permissionService : 所以我写了一个permissionService

permissions = [];

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

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

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

the getPerms() method is fired on menu button click, then in the app.component I inject the service and call the isPacksManagerEnable() on the template: 单击菜单按钮时会触发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>

Hope it helps someone with my same issue 希望它对我的问题有帮助

Issue 问题

You have issue with permissions and appCheckPermissions in directive. 您在指令中存在permissionsappCheckPermissions问题。 You are checking for permission with Asynchronous data permissions which is not available at that point of time. 您正在使用异步数据permissions检查权限,该时间在那时不可用。 Once the permissions is loaded you are not triggering the setter appCheckPermissions which is responsible to hide and show the link. 加载permissions ,您不会触发设置程序appCheckPermissions ,后者负责隐藏和显示链接。

Fix 固定

It has simple fix, you need to ensure that whenever there is change in the permissions , it should trigger the permission checks. 它具有简单的修复程序,您需要确保每当permissions发生变化时,它都应触发权限检查。

Modified version 修改版

check-permission.directive.ts 入住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();
    });
  }
}

Note : Code is written directly in stackoverflow editor so there could be typo or syntactical errors. 注意:代码是直接在stackoverflow editor编写的,因此可能存在拼写错误或语法错误。 Please fix yourself. 请自行修复。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM