Angular Directive Tutorial With Example | Custom Directives | Edureka (2023)

Become a Certified Professional

In thisblog, we will understand what isAngular Directive with sample codes to get a better understanding. Later moving ahead, we will understand Attribute & Structural Directives. We will also focus on writing custom directive in Angular. Below is the sequence in which we will cover all the topics:

  • Angular Directives
  • Attribute Directive
  • Structural Directive

You may go through this recording of Angular Directivewhere our Angular Certification Training expert has explained the topics in a detailed manner with examples that will help you to understand this conceptbetter.

Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular Training | Edureka

This Edureka “Angular Directives” tutorial will help you to learn about different directives in Angular.

Now, let us move ahead in this Angular Directive blog and first understand what is Angular Directive?

Angular Directive

You might be wondering why do we need Angular Directives. Now take a look at the below image, if you want a similar functionality in all the components for an example fade-in and fade-out functionality, you can take two approaches. The common approach would be, you can explicitly write the code in all the components for the required behavior, but it would be tedious and complex. Alternatively, like a function in a programming language, you can write the code and later you can call it anytime whenever you want that behavior of that function. Similarly, you can create a directive and write the behavior inside it. Then, wherever you need that behavior, you can import the directive.

Angular Directive Tutorial With Example | Custom Directives | Edureka (1)

Fig: Angular Directive – Why do we need Directives?

Angular Directive is basically a class with a@Directivedecorator. You might be wondering what are decorators?Decorators are functions that modify JavaScript classes. Decorators are used for attaching metadata to classes, it knows the configuration of those classes and how they should work.

You would be surprised to know that a component is also adirective-with-a-template. A@Component decorator is actually a@Directivedecorator extended with template-oriented features.Whenever Angular renders a directive, it changes the DOM according to the instructions given by the directive.Directiveappears within an element tag similar to attributes.

The Angular Directive can be classified into two types:structuralandattributedirectives.

Structuraldirectives alter layout by adding, removing, and replacing elements in DOM.

Let us briefly understand the two majorly used built-in structural directives:

<li *ngFor="let movie of movies"></li><movie-detail *ngIf="selectedMovie"></movie-detail>
  • *ngForis a looping variable thattells Angular to take one<li>per movie from the movies list.
  • *ngIfwill include the MovieDetailcomponent only if a movie is selected otherwise it will remove it from the DOM.

Attributedirectivealter the appearance or behavior of an existing element. When you include attribute directives in templates, they look like regular HTML attributes.ThengModeldirective, which implements two-way data binding, is an example of an attribute directive.ngModelmodifies the behavior of an existing element by setting its display property and responding to the changing events.

<input [(ngModel)]="movie.name">

Angular has a few more directives that either alter the layout structure (for example,ngSwitch) or modify aspects of DOM elements and components (for example,ngStyleandngClass) which I will be taking about.You can also write your own directives, i.e. Custom Angular Directive.

Now moving ahead, we will discuss attribute directive first and then structural directive to get a clear idea how they work and how to implement them.

Angular Directive: Attribute Directives

Attribute Directive is basically used to modify or alter the appearance and behavior of an element.

The selector is the property that identifies the attribute. It is used as HTML tag to target & insert an instance of the directive class where it finds that tag. The directive class implements the desired directive behavior.

Now we will create amyHighlightattribute directive to set an element’s background color when you hovers over that element.

It can be applied anywhere using the below code:

 Highlight me! 

Create the highlight directive typescript file i.e. src/app/highlight.directive.ts and embed the following code:

import { Directive, ElementRef, HostListener, Input } from '@angular/core';@Directive({selector: '[myHighlight]'})export class HighlightDirective {constructor(private el: ElementRef) { }@Input() defaultColor: string;@Input('myHighlight') highlightColor: string;@HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor || this.defaultColor || 'red');}@HostListener('mouseleave') onMouseLeave() { this.highlight(null);}private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color;}}

Theimportstatement specifies the required dependencies used from the Angularcore:

  • The directiveprovides the functionality of the@Directivedecorator.
  • ElementRef injectsinto the directive’s constructor so that the code can access the DOM element.
  • Input allows data to flow from the binding expression into the directive.

Next, the@Directivedecorator function contains the directive metadata in a configuration object as an argument.

@Directiverequires a selector to identify that the HTML in the template is associated with the directive. Here, the directive’s selector is[myHighlight]. Angular locates all elements in the template which hasan attribute namedmyHighlight.

After the@Directivemetadata, the next part in the above code is the directive’s class, calledHighlightDirective, which contains the logic for the directive. ExportingHighlightDirectivemakes it accessible to other components.

Angular creates a new instance of the directive’s class for each matching element, injecting an AngularElementRefinto the constructor.ElementRefis a service that grants direct access to the DOM element through itsnativeElementproperty.To use the newmyHighlight, inside a template that applies the directive as an attribute to a paragraph (<p>) element. The<p>element is the attributehost.

Put the template in the app component file or you can also create a new HTML file and provide its path in templateURL:

import { Component } from '@angular/core';@Component({selector: 'my-app',template: `<h1>My First Attribute Directive</h1><h4>Pick a highlight color</h4><div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan</div> Highlight me! Highlight me too! ` }) export class AppComponent { } 

Next, add animportstatement to fetch theHighlightdirective and add that class to thedeclarationsNgModule metadata. This way Angular recognizes the directive when it encountersmyHighlightin the template. In the below code you can see how to import the directive in the main module.

import { NgModule } from '@angular/core';import { BrowserModule } from '@angular/platform-browser';import { HighlightDirective } from './highlight.directive';import { AppComponent } from './app.component';@NgModule({imports: [ BrowserModule ],declarations: [ AppComponent, HighlightDirective ],bootstrap: [ AppComponent ]})export class AppModule { }

Now when the app runs, the myHighlight directive highlights the paragraph text, angular detects that you’re trying to bind to something and finds this directive in the module’s declarations array. After specifying HighlightDirective in the declarations array, Angular knows it can apply the directive to components declared in this module.

To summarize, Angular found themyHighlightattribute on the<p>element. It created an instance of theHighlightDirectiveclass and injected a reference to the<p>element into the directive’s constructor which sets the<p>element’s background style.

Again, looking back at the directive code, we are adding two eventhandlers that respond when the mouse enters or leaves, each adorned by theHostListenerdecorator.

import { Directive, ElementRef, HostListener, Input } from '@angular/core';@Directive({selector: '[myHighlight]' })export class HighlightDirective {constructor(private el: ElementRef) { }@Input() defaultColor: string;@Input('myHighlight') highlightColor: string;@HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor || this.defaultColor || 'red');}@HostListener('mouseleave') onMouseLeave() { this.highlight(null);}private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color;}}

The@HostListenerdecorator lets you subscribe to events of the DOM element that hosts an attribute directive.

The handlers delegate to a helper method that sets the color on the DOM element,el, which you declare and initializes in the constructor.

Notice the@Inputdecorator. It adds metadata to the class that makes the directive’shighlightColorproperty available for binding. It’s called aninputproperty because data flows from the binding expressionintothe directive. Without that input metadata, Angular rejects the binding.

The[myHighlight]attribute binding both applies the highlighting directive to the<p>element and sets the directive’s highlight color with a property binding. We’re re-using the directive’s attribute selector ([myHighlight]) to do both jobs.

@Input('myHighlight') highlightColor: string;

Insidethe directive, the property is known ashighlightColor.Outsidethe directive, where you bind to it, it’s known asmyHighlight.

<h1>My First Attribute Directive</h1><h4>Pick a highlight color</h4><div><input type="radio" name="colors" (click)="color='lightgreen'">Green<input type="radio" name="colors" (click)="color='yellow'">Yellow<input type="radio" name="colors" (click)="color='cyan'">Cyan</div> Highlight me! Highlight me too! 

In the above code section, you’ll turnAppComponentinto a harness that lets you pick the highlight color with a radio button and bind your color choice to the directive. Angular knows that thedefaultColorbinding belongs to theHighlightDirectivebecause you made itpublicwith the@Input decorator.

@Input('myHighlight') highlightColor: string;

Either way, the@Inputdecorator tells Angular that this property ispublicand available for binding by a parent component. Without@Input, Angular refuses to bind to the property.

The component and its template trust each other implicitly. Therefore, the component’s own template may bind toanyproperty of that component, with or without the@Input decorator. But a component or directive shouldn’t blindly trustothercomponents and directives. The properties of a component or a directive are hidden from binding by default. They areprivatefrom an Angular binding perspective. When adorned with the@Inputdecorator, the property becomespublicfrom an Angular binding perspective. Only then can it be bound by some other component or directive.

When a property appears in the template expression to therightof the equals (=), it belongs to the template’s component and does not require the@Inputdecorator. When it appears insquare brackets([ ]) to theleftof the equals (=), the property belongs to someothercomponent or directive; that property must be adorned with the@Inputdecorator.

<span style="color: #ff0000;"></pre></span> Highlight me! 

Now looking at the above code you can easily infer that:

The colorproperty in the expression on the right belongs to the template’s component. The template and its component trust each other. Thecolorproperty doesn’t require the@Input

The myHighlightproperty on the left refers to analiasedproperty of theHighlightDirective, not a property of the template’s component. There are trust issues. Therefore, the directive property must carry the@Input decorator.

Now after discussing Attribute Directives in this Angular Directive blog, we should move ahead to the Structural Directive.

Angular Directive: Structural Directives

Structural directives are responsible for shape or reshape the DOM’sstructure, typically by adding, removing, or manipulating elements.Similar to other directives, you apply a structural directive to ahost element. The directive then does whatever it’s designed to do with that host element.Structural directives are easy to recognize. An asterisk (*) precedes the directive attribute name. It does not require brackets or parentheses like attribute directive.

Three of the common, built-in structural directives are NgIf, NgFor, and NgSwitch. Let us take a look how they look in a template:

<div *ngIf="movie" >{{movie.name}}</div><ul>&nbsp; <li *ngFor="let movie of movies">{{movie.name}}</li></ul><div [ngSwitch]="movie?.genre">&nbsp; <action-movie *ngSwitchCase="'action'" [movie]=" movie "></action-movie>&nbsp; <horror-movie *ngSwitchCase="'horror'" [movie]=" movie "></horror-movie>&nbsp; <thriller-movie *ngSwitchCase="'thriller'" [movie]=" movie "></thriller-movie>&nbsp; <unknown-movie *ngSwitchDefault [movie]=" movie "></unknown-movie></div>

The Directives can be writtenin bothUpperCamelCaseandlowerCamelCase. This is becauseNgIfrefers to the directiveclass &ngIfrefers to the directive’sattribute name.When we talk about its properties and what the directive does, we will refer to the directiveclass. Whereas we will refer to theattribute namewhen describing how you apply the directive to an element in the HTML template.

NgIf

NgIfis the simplest structural directive and the easiest to understand. It takes a boolean expression and makes an entire chunk of the DOM appear or disappear. You can assume it similar as if statement in a programming language

 Expression is true and ngIf is true. This paragraph will be included in the DOM. Expression is false and ngIf is false. This paragraph will not be included in the DOM. 

ThengIfdirective doesn’t hide elements. It adds and removes them physically from the DOM. You can confirm it by using browser developer tools to inspect the DOM.When the condition is false,NgIfremoves its host element from the DOM, detaches the component from Angular change detection, and destroys it.

Do not get confused between removingandhiding. The difference between hiding and removing doesn’t matter for a simple paragraph. But it does matter when the host element is attached to a resource-intensive component. The component stays attached to its DOM element. It keeps listening to events. Angular keeps checking for the changes that could affect data bindings.

Although the element is invisible, the component listens to the DOM. The performance and memory burden can be substantial, responsiveness can degrade, and the user sees nothing.On the positive side, showing the element again is quick. The component’s previous state is preserved and ready to display. The component doesn’t re-initialize—an operation that could be expensive. So hiding and showing is sometimes the right thing to do.

But if you want to remove the DOM element that the user can’t see and recover the unused resources, in such situation you use the structural directive likeNgIf.

Why we use Asterisk (*)?

Surely you noticed the asterisk (*) prefix to the directive name and wondered why it is important?Here *ngIf is displaying the hero’s name ifheroexists.

<div *ngIf="movie">{{movie.name}}</div>

Internally, Angular decouples it in two stages. First, it translates the*ngIf=”…”into a template attribute, template = “ngIf …” and then it becomes:

<div template="ngIf movie">{{movie.name}}</div>

Then it translates the templateattributeinto a<ng-template>element, wrapped around the host element as shown below.

<ng-template [ngIf]="movie"><div>&nbsp; &nbsp; {{movie.name}} </div></ng-template>
  • The *ngIfdirective moved to the<ng-template>element where it became a property binding,[ngIf].
  • The rest of the<div>, including its class attribute, moved inside the<ng-template>

None of these forms are actually rendered. Only the finished product ends up in the DOM.

The NgFor and NgSwitch directives follow the same pattern.

*ngFor

Angular transforms the*ngForin similar fashion from asterisk (*) syntax through templateattributeto<ng-template>element.

It looks like this:

<div *ngFor="let movie of movies">{{movie.name}}</div><div template="ngFor let movie of movies">{{movie.name}}</div><ng-template ngFor let-movie [ngForOf]="movies">{{movie.name}}</ng-template>

ngForneeds a looping variable (let movie) and a list (movies).EverythingoutsidethengForstring stays with the host element (the<div>) as it moves inside the<ng-template>.

The Angular microsyntax lets you configure a directive in a compact, friendly string. The microsyntax parser translates that string into attributes on the<ng-template>:

  • The letkeyword declares atemplate input variablethat you reference within the template. The input variables in this example is movie. The parser translateslet movieinto variables named,let-movie.
  • As theNgFordirective loops through the list, it sets and resets properties of its owncontext

Template input variable

Atemplate input variableis a variable whose value you can referwithina single instance of the template. All are preceded by the keywordlet.

You declare a templateinputvariable using theletkeyword (let hero). The variable’s scope is limited to asingle instanceof the repeated template. You can use the same variable name again in the definition of other structural directives.

You can only apply one structural directive per host element. For an example, if you want to repeat a block of HTML but only when a particular condition is true. You’lltryto put both an*ngForand an*ngIfon the same host element. Angular won’t let you. You may apply only onestructuraldirective to an element.

The reason is simplicity. Structural directives can do complex things with the host element. When two directives lay claim to the same host element, which one takes precedence? Which should go first, theNgIfor theNgFor?

It is hard to answer these questions. There’s an easy solution for this use case: put the*ngIfon a container element that wraps the*ngForelement. One or both elements can be anng-container. So if you don’t have to introduce extra levels of HTML, you can use NgSwitch in such conditions.

NgSwitchDirective

The AngularNgSwitchis actually a set of cooperating directives:NgSwitch,NgSwitchCase, andNgSwitchDefault.

Here’s an example.

<div [ngSwitch]="movie?.genre">&nbsp; &nbsp; <action-movie *ngSwitchCase="'action'" [movie]=" movie "></action-movie>&nbsp; &nbsp; <horror-movie *ngSwitchCase="'horror'" [movie]=" movie "></horror-movie>&nbsp; &nbsp; <thriller-movie&nbsp;*ngSwitchCase="'thriller'" [movie]=" movie "></thriller-movie> &nbsp;&nbsp; <unknown-movive *ngSwitchDefault [movie]=" movie "></unknown-movie></div>

The switch value assigned toNgSwitch(movie.genre) determines which of the switch cases are displayed.

NgSwitchCaseandNgSwitchDefaultarestructural directives. You attach them to elements using the asterisk (*) prefix notation. ANgSwitchCasedisplays its host element when its value matches the switch value. TheNgSwitchDefaultdisplays its host element when no siblingNgSwitchCasematches the switch value.

The element to which you apply a directive is itshostelement. The<action-movie>is the host element for the happy*ngSwitchCase. The<unknown-movie>is the host element for the*ngSwitchDefault.

<div [ngSwitch]="movie?.genre">&nbsp; <action-movie *ngSwitchCase="'action'" [movie]=" movie "></action-movie>&nbsp; <horror-movie *ngSwitchCase="'horror'" [movie]=" movie "></horror-movie>&nbsp; <thriller-movie *ngSwitchCase="'thriller'" [movie]=" movie "></thriller-movie>&nbsp; <unknown-movive *ngSwitchDefault [movie]=" movie "></unknown-movie></div>

Internally, it is also converted into the<ng-template>element form. Then it looks like:

<div [ngSwitch]="movie?.genre">&nbsp; <ng-template [ngSwitchCase]="'action'">&nbsp;&nbsp;&nbsp; <action-movie [movie]="movie"></ action-movie >&nbsp; </ng-template>&nbsp; <ng-template [ngSwitchCase]="'sad'">&nbsp;&nbsp;&nbsp; <horror-movie [movie]="movie"></horror-movie>&nbsp; </ng-template>&nbsp; <ng-template [ngSwitchCase]="'confused'">&nbsp;&nbsp;&nbsp; <thriller-movie [movie]="movie"></thriller-movie>&nbsp; </ng-template >&nbsp; <ng-template ngSwitchDefault>&nbsp;&nbsp;&nbsp; <unknown-movie [movie]="movie"></unknown-movie></ng-template></div>

The <ng-template> is an Angular element for rendering HTML. It is never displayed directly. In fact, before rendering the view, Angularreplaces the<ng-template>and its contents with a comment.

Now in this Angular Directive blog, let us look how you can write a custom structural directive.

Custom Structural Directive

For writing a custom structural directive:

  • Import the Directivedecorator (instead of theComponentdecorator).
  • Import the Input,TemplateRef, andViewContainerRefsymbols; you’ll need them foranystructural directive.
  • Apply the decorator to the directive class.
  • Set the CSSattribute selectorthat identifies the directive when applied to an element in a template.

This is how you start creating a directive:

import{Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';@Directive({selector: '[myCustom]'})export class myCustomDirective {}

The directive’sselectoris typically the directive’sattribute namein square brackets,[myCustom]. The directiveattribute nameshould be spelled inlowerCamelCaseand begin with a prefix. Don’t useng. That prefix belongs to Angular. The directiveclassname ends inDirective

TemplateRefandViewContainerRef

A simple structural angular directive creates anembedded viewfrom the Angular-generated<ng-template>and inserts that view in a view containeradjacent to the directive’s original<p>host element.You’ll acquire the<ng-template>contents with aTemplateRefand access theview containerthrough aViewContainerRef.You inject both in the directive constructor as private variables of the class.

I hope this blog was informative and added value to you. Now, youmust be clear about Angular Directive and ready to work with theAngular application. I would recommend you to go through this Angular TutorialEdureka video playlist to watch videos and learn how to work with the Angular applications.

Master Flutter’s powerful features and create stunning user interfaces in a Flutter Training.

Now that you know the Angular Directive, check out theAngular Trainingby Edureka,a trusted online learning companywith a network of more than250,000satisfied learnersspread acrossthe globe.Angular is a JavaScript framework which is used to create scalable, enterprise, and performance client-side web applications. With Angular framework adoption being high, performance management of the application is community driven indirectly driving better job opportunities. The Angular Certification Training aims at covering all these new concepts around Enterprise Application Development.

Top Articles
Latest Posts
Article information

Author: Barbera Armstrong

Last Updated: 11/25/2022

Views: 5506

Rating: 4.9 / 5 (59 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Barbera Armstrong

Birthday: 1992-09-12

Address: Suite 993 99852 Daugherty Causeway, Ritchiehaven, VT 49630

Phone: +5026838435397

Job: National Engineer

Hobby: Listening to music, Board games, Photography, Ice skating, LARPing, Kite flying, Rugby

Introduction: My name is Barbera Armstrong, I am a lovely, delightful, cooperative, funny, enchanting, vivacious, tender person who loves writing and wants to share my knowledge and understanding with you.