Angular - Custom Structural Directives
I've sometimes asked myself what are *ngIf
and *ngFor
in Angular but never dig into it before until I faced a case where none of the knowledge I had about Angular concept at that time (services, validators, guards, interceptors, directives) would give me the answer I needed.
What I wanted to do, was an *ngIf
like element that I could call in my component to manage my user permissions, something that I would call: *ifHasPermission
It's by searching for more details about *ngIf
that I discovered: Structural Directives
What is this?
Structural Directives are the handyman that change the structure of the DOM by adding or removing elements from it.
They are prefixed with *
and if you're using Angular I'm sure you know some of them: *ngIf, *ngFor
are the most well known.
How to make one?
Let's create our *ifHasPermission
structural directive.
I want to be able to use this structural directive like so:
<button *ifHasPermission="user.features.includes('PROFILE')"></button>
For each element of my view to be displayed only if the user feature array contains the required element.
Let's begin with the empty shell of a classic directive
:
import { Directive, Input } from "@angular/core";
@Directive({
selector: "[ifHasPermission]",
})
export class IfHasPermissionDirective {
constructor() {}
@Input() set ifHasPermission(condition: boolean) {}
}
Here nothing complexe a simple @Directive
decorator with a selector
param defining the name of your directive (don't forget the [ ]
).
Then a class IfHasPermissionDirective
contains an empty constructor
and a ifHasPermission
function.
If you're not familiar with the @Input set
syntax you can undestand it like so:
@Input
: Parent to child data transmission, in our case it means that the component we will use theifHasPermission
on will pass some data to ourIfHasPermissionDirective
set
: on change, the property will be equal to the new value
Now let's add the magic :
import { Directive, Input, TemplateRef, ViewContainerRef } from "@angular/core";
@Directive({
selector: "[ifHasPermission]",
})
export class IfHasPermissionDirective {
constructor(
private readonly templateRef: TemplateRef<any>,
private readonly viewContainer: ViewContainerRef
) {}
@Input() set ifHasPermission(condition: boolean) {
condition
? this.viewContainer.createEmbeddedView(this.templateRef)
: this.viewContainer.clear();
}
}
Let's break down this code:
In the constructor
we inject two private elements in readonly.
TemplateRef
: represents an embedded template that can be used to instantiate embedded views.ViewContainerRef
: represents a container where one or more views can be attached to a component.
Now in ifHasPermission()
we use a ternary to say:
if(I have the permission){
"Display the element"
}else{
"Don't display the element"
}
And TADAM! You now have a structural directive that you can use in all your application to conditionally display or hide the element of your page.
Subscribe to my newsletter
Read articles from Guileas directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by