Creating our own Structural Directive in Angular

Have you ever wondered, why do we place * with the structural directives like *ngFor, *ngIf, *ngSwitchCase because this indicates angular that these are structural directives.
Structural directives with start(*) are the pretties way to use them but behind the scenes, angular transform them into something else.
Lets figure out.
we have some already written code where we used *ngIf
<div *ngIf="!onlyOdd">
<li class="list-group-item" *ngFor="let num of evenNums">
{{num}}
</li></div>
we would use ng-template and inside this we would be placing some content which needs to be loaded conditionally. An ng-template is an element which iteself is not rendered but it allows us to define a template a the end for Angular to use once it determines that this condition is true. So we will place a ngIf on ng-template in a square barcket but without any star(*)
<ng-template [ngIf]="!onlyOdd">
<div>
<li class="list-group-item" *ngFor="let num of evenNums">
 {{num}}
</li>
</div>
</ng-template>
This is what it would be getting later transformed into when we place start(*) with structural directives.
Lets create our own structural directives
Now we have some idea about how structural directives works behind the scene, we would be building our own structural directives and will name it
appUnless
appUnless is opposite of *ngIf directive and appUnless will only attach something when the condition is false.
ng generate directive unless
This is our skeleton of appUnless directive
import {
    Directive
} from '@angular/core';
@Directive({
    selector: '[appUnless]'
})
export class UnlessDirective {
    constructor() {}
}
Lets start building it….
import {
    Directive,
    Input,
    TemplateRef,
    ViewContainerRef
} from '@angular/core';
@Directive({
    selector: '[appUnless]'
})
export class UnlessDirective {
    @Input() set appUnless(condition: boolean) {
        if (!condition) {
            this.vcRef.createEmbeddedView(this.templateRef)
        } else {
            this.vcRef.clear();
        }
    }
    constructor(private templateRef: TemplateRef < any > ,
        private vcRef: ViewContainerRef) {}
}
Now let me the explain you the above code.
first we need to get some condition as an input. So, we added @Input() decorator. Then we want to bind it to a property named appUnless not directly but using set keyword which belongs to Typescript’ setter and getter method. So whenever the value bound to this input property changes it will trigger a method which expects an argument which is type of boolean which will be our condition at our end.
@Input() set appUnless(condition: boolean) {}
In the next line, we have to check if the condition is not true that means false and then we will display something or else nothing in the case of true.
if (false) { //display the content }
 else { //display nothing}
Now important thing to remember is , our directive appUnless will be placed on a DOM element which will be transformed into ng-template at the end if we use * with directive.
<div *appUnless="some condition"> </div>
//will tranformed behind the scene to
<ng-template [appUnless]="some condition"> </ng-template
So, we want to get access to this template and along with the access to the place in the document where we want to render it.
We can get access to those but with injecting something.
Now to get access to the Element we injected ElementRef. In the same way to get access to the template we will be injecting TemplateRef. Don’t forget to import it from ‘@angular/core’. And the second we need is ViewContainer.
What we need to render (template access) is provided by TemplateRef and where do we need to render is provided by ViewContainer.
import {
    TemplateRef,
    ViewContainerRef
} from '@angular/core';
constructor(private templateRef: TemplateRef < any > ,
    private vcRef: ViewContainerRef) {}
}
Now we have some method called createEmbeddedView() to call whenever our condition changes. Now this method will create a view in a view container and view is simply our templateRef
if (condtion is false) {
    this.vcRef.createEmbeddedView(this.templateRef)
} else {
    this.vcRef.clear();
}
So the template will be placed in side the view container only when the condition is false since we are building a directive just opposite to *ngIf and if the condition is true then we will call clear() method on View Container to clear our view because we dont need to render in that case.
with this if you place this directive on the any DOM elements, it will work as expected. Dont forget to add start(*) with the directive since what we have build now is structural directive
<h2 *appUnless=”some condition”> Hello there!<h2>
Note: our Directive name and the property to which we are binding our value in the class should be same or else it won’t work as expected.
@Input() set Hello () {}
<h2 *Hello=""> Hello There!</h2>
Thanks for reading this article. Please let me know your suggestions in the comment box.

People Reaction : 0

Rohit Sharma
Name : Email : Website :
© 2020 WriteSomeCode. All Right Reserved. A Rohit Sharma Blog. Creative Commons License licensed under a Creative Commons Attribution 4.0 International License