Saturday, June 16, 2018

Custom *ngIf directive

Hi,

Case scenario: Parts of your web-site should consider user permission.


Goal: Logged-in users can see parts that logged-out users cannot and vice-versa.


If you already wrote a basic angular application - then you are probably familier with *ngIf stractural directive which conditionally includes a template based on the value of an expression (Official doc: https://angular.io/api/common/NgIf).

In order to achieve our goal we can simply use this kind of *ngIf:


<div *ngIf="authService.isLoggedIn">
      <h2>Logged In</h2>
</div>
<div *ngIf="!authService.isLoggedIn">
      <h2>Not Logged</h2>
</div>

What bothers here ?
This *ngIf would be used across many components and AuthService would have to be injected to each component.


Solution: Custom *ngIf directive


Searching through the net i've encountered this question:
https://stackoverflow.com/questions/43517660/directive-that-works-as-ng-if-angular-2

The accepted answer may lack some functionality because it's not using angular ngIf.

Here is a directive which uses ngIf :




app.ts


<div *allowed="true">
      <h2>Logged In</h2>
    </div>
    <div *allowed="false">
      <h2>Not Logged</h2>
    </div>

 Usage of allowed directive is pretty simple.

allowed.directive.ts


private _allowed: boolean;
  private ngIfDirective: NgIf;
  private isLoggedInSubs: Subscription;
  
  @Input()
    public set allowed(val: boolean) {
        this._allowed = val;
        this.setNgIf();
    };
  
  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, 
  private authService: AuthService) {
    if(!this.ngIfDirective) {
          this.ngIfDirective = new NgIf(this.viewContainer,this.templateRef);
    }
        
    this.isLoggedInSubs = this.authService.isLoggedInChanged.subscribe((value) => { 
      this.setNgIf();
    });
  }
  
  setNgIf() {
    this.ngIfDirective.ngIf = this.authService.authModel.isLoggedIn == this._allowed;
  }

constructor needs templateRef and viewContainer references from angular injector in order to create ngIf directive instance. In addition, authService is injected in order to notify the directive about changes of isLoggedIn property.

Each time isLoggedIn property changes - ngIf property of ngIfDirective needs to be re-set.

auth.service.ts


public isLoggedInChanged: Subject<string> = new Subject<string>();
  
  constructor() {
    this.authModel = new AuthModel();
  }
  
  set isLoggedIn(val: boolean) {
    this.authModel.isLoggedIn = val;
    this.isLoggedInChanged.next(val);
  }
  
  get isLoggedIn(): boolean {
    return this.authModel.isLoggedIn;
  }
  
  public switchLoggedIn() {
    this.isLoggedIn = !this.isLoggedIn;
  }

In authService, after isLoggedIn is set - isLoggedInChanged subject emits the new value using next function.

5 comments:

Thank you Blogger, hello Medium

Hey guys, I've been writing in Blogger for almost 10 years this is a time to move on. I'm happy to announce my new blog at Med...