How to create a directive in Angular that renders components only if user is authorized?
Tags: angular, typescript, directive, authorization
Published: 2023-01-18 12:43:49

---
title: How to create a directive in Angular that renders components only if user is authorized?
published: true
date: 2023-01-17 15:39:33 UTC
tags: angular, typescript, directive, authorization
canonical_url: https://potatoes.hashnode.dev/how-i-created-a-directive-in-angular-that-renders-components-only-if-user-is-authorized
---

Recently, I was working on an Angular project where certain components were public meaning that everyone should see them and others should be rendered only if the user is authenticated or have a specific role, so I was doing something like this:

```html
<!-- file name: offers.component.html -->
<div>
    <app-nav-bar></app-nav-bar>
    <app-profile-image *ngIf="user.isAuthenticatd"
    ></app-profile-image>
    <div>
        <app-search></app-search>
        <app-offers-list></app-offers-list>
    </div>
    <div>
        <app-review-offer *ngIf="user?.roles.include('AGENT')"
        ></app-review-offer>
    </div>
<div>

```

This doesn't sounds clean to me, especially the last one: `*ngIf="user?.roles.include('AGENT')"`

Thankfully Angular gives us the ability to create our custom directives. so let's create one!

First, we need to create a class and annotate it with `@Directive` annotation (Angular CLI made it easy [😋](https://getemoji.com/))

```bash
ng generate directive authorized 
# or use the shourhand: ng g d authorized

```

The above command will generate a class as follows:

```typescript
// file name: authorized.directive.ts
import { Directive } from '@angular/core';

@Directive({
  selector: '[appAuthorized]'
})
export class AuthorizedDirective {

  constructor() { }

}

```

Remember we need to pass/bind the role as an input to use it in our directive something like this:

```html
<div>
    <app-review-offer *appAuthorized="'AGENT'"></app-review-offer>
</div>

```

Like in component classes, we will create a `property` and annotate it with the `@Input()` decorator.

```typescript
// file name: authorized.directive.ts
import { Directive, Input } from '@angular/core';

@Directive({
  selector: '[appAuthorized]'
})
export class AuthorizedDirective {
  @Input appAuthorized

  constructor() { }

}

```

Now we want to perform our login whenever the `appAuthorized` input is changed, for this, we will use a setter:

```typescript
// file name: authorized.directive.ts
import { Directive, Input } from '@angular/core';

@Directive({
  selector: '[appAuthorized]'
})
export class AuthorizedDirective {
  @Input set appAuthorized(role: string){
      console.log(role) // this will print: AGENT
  }

  constructor() { }

}

// file name: offers.component.html
//...
<div>
    <app-review-offer *appAuthorized="'AGENT'"></app-review-offer>
</div>
//...
//...

```

The above code will simply print `AGENT` in the console

**\*\* Note that** `appAuthorized` **is still a property** [learn more about component-interaction](https://angular.io/guide/component-interaction)

To proceed we need to inject some objects:

1. [_TemplateRef_](https://angular.io/api/core/TemplateRef) : to access our template

2. [_ViewContainerRef_](https://angular.io/api/core/ViewContainerRef) : to create a new embedded view.

3. _AuthService_: This one could be different from one project to another, for me, I have a service called `authService` where I had stored the currently logged-in user with all its information such as username, roles...

```typescript
import {Directive, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
import {AuthService} from "../services/auth/auth.service";
import {Principal} from "../models/principal";

@Directive({
  selector: '[appAuthorized]'
})
export class AuthorizedDirective {

   @Input set appAuthorized(role: string){
      console.log(role) // this will print: AGENT
   }

  constructor(
    private auth: AuthService,
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef
  ) { }

}

```

And finally, add the logic that handles the authorization:

```typescript
import {Directive, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
import {AuthService} from "../services/auth/auth.service";
import {Principal} from "../models/principal";

@Directive({
  selector: '[appAuthorized]'
})
export class AuthorizedDirective {

  @Input() set appAuthorized(role: string){
    this.auth.principal$.subscribe({
      next:(value)=>{
        // Check if user is authenticated
        if(!(<Principal>value).authenticated){
          this.viewContainerRef.clear();
          return;
        }

        // Check if the user have the given role
        if (role !== '*' && !(<Principal>value).roles.includes(`ROLE_${role}`)){
          this.viewContainerRef.clear();
          return;
        }

        this.viewContainerRef.createEmbeddedView(this.templateRef)
      }
    })

  }

  constructor(
    private auth: AuthService,
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef

  ) { }

}

```

As you noticed in the above code we used the clear method `this.viewContainerRef.clear()` to destroy the view if the user is not authenticated or doesn't have the desired role.

We also used `this.viewContainerRef.createEmbeddedView(this.templateRef)` to instantiate an embedded view and inserts it into our container.

Now we can use our directive anywhere in our application:

```html
<!-- file name: offers.component.html -->
<div>
    <app-nav-bar></app-nav-bar>
    <app-profile-image *appAuthorized="'*'"
    ></app-profile-image>
    <div>
        <app-search></app-search>
        <app-offers-list></app-offers-list>
    </div>
    <div>
        <app-review-offer *appAuthorized="'AGENT'"
        ></app-review-offer>
    </div>
<div>

```

That was it, I hope you found it helpful. If you have any feedback or suggestions for improvements, I would like to hear from you.