What is New in Angular 10?

In this article we are going to learn how to create a Role-Based Show/Hide Directive in Angular. If you are in the Angular realm for a good long time, you must be familiar with the concept of directives. Directives are one of the basic building blocks of Angular. It is everywhere. Even components in Angular are in directives with a template.

Directives basically are typescript classes with @Directive decorators. From the Angular documentation, you can see there are three types of directives

  1. Components: directives with a template.
  2. Structural directives: change the DOM layout by adding and removing DOM elements.
  3. Attribute directives: change the appearance or behavior of an element, component, or another directive.

Some common directives that must be familiar to you are: *ngFor(structural)*ngIf(structural)hidden(attribute)NgStyle(attribute), etc. The scope of this article will be on the last type: Attribute directives.

Recently on one of my projects, I had a requirement for a simple role-based access system. In one of my modules, the add, edit, delete, or update actions were based on the user role which is determined at the admin level and is configurable. The access information is available on the user login.

How would you implement this?

Initial Thoughts

Initial thoughts were to use *ngIf or [hidden] directives in all components with custom logic inside the component’s controller. It looked easy to implement. But how reusable would it be? What if you want to use it in multiple modules, components? Yes, time to introduce a directive!


Create a Custom Angular Directive

Like I stated above, directives are typescript classes. You can create a file, conveniently name it access-control.directive.ts and create your class manually. But I am going to take help from Angular CLI to do this.

ng generate directive access-control

You now have the simplest directive ever, which doesn’t do anything just yet.

import { Directive } from "@angular/core";

@Directive({
  selector: "[accessControl]",
})
export class AccessControlDirective {
  constructor() {}
}

Before we jump in and start editing our directive, let’s have a look at our access control data that we received from our REST API.

{
  "access_controls": [
    {
      "module_name": "users",
      "create_action": false,
      "read_action": true,
      "update_action": true,
      "delete_action": false
    },
    {
      "module_name": "articles",
      "create_action": true,
      "read_action": true,
      "update_action": false,
      "delete_action": false
    }
  ]
}

From the data above, it is evident that our directive needs to get at least two pieces of information from the host, (the component where the directive is used):

  1. Type of module (users or articles)
  2. Type of access (create, edit, delete, or read)

Let’s take a step back and recall that components are indeed directives, thus you can pass data to a directive the same way as you do to a component. We can use a @Input decorator. We also need to implement NgOnInit in our directive for us to be able to check access controls on the initialization of the component.

import { Directive, Input, OnInit } from "@angular/core";

@Directive({
  selector: "[accessControl]",
})
export class AccessControlDirective implements OnInit {
  @Input("moduleType") moduleType: string;
  @Input("accessType") accessType: string;
  constructor() {}

  ngOnInit() {
  }
}

Great! Let’s implement our logic to conditionally show/hide the host element, (the component our directive is hosted on).

import { Directive, Input, OnInit, ElementRef } from "@angular/core";
import { AuthService } from "./auth.service";

@Directive({
  selector: "[accessControl]",
})
export class AccessControlDirective implements OnInit {
  @Input("moduleType") moduleType: string;
  @Input("accessType") accessType: string;
  constructor(private elementRef: ElementRef, private auth: AuthService) {}

  ngOnInit() {
    this.elementRef.nativeElement.style.display = "none";
    this.checkAccess();
  }
   checkAccess() {
    const accessControls: any = this.auth.getAccessControls();
    const module: any = accessControls.find(access => access.module_name === this.moduleType);
    this.elementRef.nativeElement.style.display = module[this.accessType] ? "block" : "none";
  }
}

We have imported ElementRef from @angular/core which we can use to access DOM elements and manipulate. Be careful when you use ElementRef, as you are accessing DOM elements directly, which can attract XSS attacks. On the component initialization, we are fetching role data, crosschecking with our module and access type combination, and making the element show/hide.

Note: I am fetching the access data from the AuthService class which is an Angular service I created. You can have your custom login to do the same.

Let’s now see how we can use this in one of our components.

<button
        accessControl
        moduleType="Users"
        accessType="create_action"
      > +
 </button>

As easy as that! The button above will now show/hide based on the user access controls. You can use our accessControl directive in any of the create, edit, delete, read navigation buttons/components. It’s highly reusable.

There are plenty of other use cases for directives. You can do things like listening for events on the host and reacting to the same, apply styles to the host, and more. I hope you will create something beautiful with it.

Happy Coding!

Angular HTTP Interceptors

Angular HTTP Calls

Angular Component Communication

LEAVE A REPLY

Please enter your comment!
Please enter your name here