Creating Structural Directive In Angular

Introduction

We have two types of directives in Angular: Structural and Attribute directive. Structural directives are used for modifying the layout or structure of HTML. These directives remove or add a template, modify the template and manipulate as well. Asterisk(*) is the prefix for structural directives as Angular decouple it with <ng-template> syntax which is a wrapper for host element and its child elements. Now we will see how to create our own custom structural directives in Angular.

Creating Structural Directive

We will create a structural directive as follows:
  • Import @directive decorator and specify the name of the custom directive selector property. 
  • Apply this decorator with the directive class.
  • For structural directive, we will need ViewContainerRef and TemplateRef. Import these from @angular/core.

ViewContainerRef and TemplateRef

ViewContainerRef is a container in which views can be attached. We will use this container to attach embedded views that can be added using different methods of this class. ViewContainerRef is used to write custom structural directives. 
TemplateRef is the reference for a template. We will get the reference to the view which we need to embed in the ViewContainerRef container. We can have many embedded views using this reference. 
We can get a template reference when we specify a directive on <ng-template> or directive prefixed with an asterisk(*).
We will see an example where we are decorating the class with @Directive decorator. The custom directive name is appNgCondition.

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

@Directive({
  selector: '[appNgCondition]'
})
export class NgConditionDirective {

  constructor() { }
  
}

Now we will import ViewContainerRef and TemplateRef.  We will just see how we can add a template reference to the ViewContainerRef using the createEmbeddedView() method. 

import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';

@Directive({
  selector: '[appNgCondition]'
})

export class NgConditionDirective implements OnInit{

constructor(
  private tempRef: TemplateRef<any>,
  private VCR: ViewContainerRef) { }
  
}
  ngOnInit(){
    this.VCR.createEmbeddedView(this.tempRef);
  }

If our custom directive expects to have any input such as a condition or some number, then we need to decorate our directive with @Input decorator. We will write a custom structural directive where we want to create some directive somewhat different from ngIf.

import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';

@Directive({
  selector: '[appNgCondition]'
})

export class NgConditionDirective implements OnInit{

constructor(
  private tempRef: TemplateRef<any>,
  private VCR: ViewContainerRef) { }
  
}

 @Input() set appNgCondition(condition:boolean){

   if(!condition){
    this.VCR.createEmbeddedView(this.tempRef); // This method will add the template reference into the container
   } else{
     this.VCR.clear(); // This method will empty the ViewRef container 
   }
}

We can use this directive on an element using *appNgCondition or applying as a wrapper to that element. 

<div *appNgCondition="some condition">
  Hello World
</div>

We need to add this directive to app.module.ts file as follows:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { AppNgCondition }  from './directives/cp-if.directive';


@NgModule({
  imports: [     
        BrowserModule,
 
  ],
  declarations: [
     AppComponent,
     AppNgCondition 
  ],
  bootstrap: [AppComponent]
})
export class AppModule { } 

Comments