Angular directive makes more sense when used in an example. The interesting example will make things fun to understand and follow. In our scenario we will handle the images that are failed to load.
If you are more of a video person that I have also attached the video the end of the article
Watch VideoIntroduction
Angular directive add super power to HTML. In other terms it extends the functionality of HTML elements.
There are two types of angular directives.
- Attribute Directive
- Structual Directive
Attribute Directive basically modifies the appearance and behavior of DOM element. Styling a background color of paragraph tag is one of the example. Structural Directive do things like wrapping elements and manipulation for structural change or Layout. For the sake of this article, we will use the former type.
Creating Custom Angular Attribute Directive
First of all let’s create a blank angular app
ng new sample-app
// after going through the interactive CLI which might asks for
// whether we need angular routing and CSS pre-processor to choose
After serving the app we are welcome with this screen

Use case
What happen when an HTML img fails to load. Browser displays the alt or alternate text instead. What if we have away to load a placeholder/fallback image. That where our directive will comes into play and that what we are making next.
Let’s go to the terminal and generate new directive.
ng g directive imgFallback
This command will generate a directive, add it to the declarations block entry module i-e app.module.ts (if no module is specified in the command)
Now let’s modify our newly generated directive
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appImgFallback]'
})
export class ImgFallbackDirective {
constructor(el: ElementRef) {
console.log(el.nativeElement);
}
}
The constructor receives the reference of current element where directive is used. We will log the current element to console. Lets use this Angular Directive by modifying app.component.html
<div>
<img src="http://placekitten.com/g/200/300" appImgFallback alt="Placekitten Placeholder">
</div>
Previously I have use lorempixel for placeholder but that does not work for some reason. I have borrowed a cat from placekitten.

Let’s fail our img by modifying the src to url that does not exist.
<div>
<img src="http://urldoesnotexist.com/g/200/300" appImgFallback alt="Placekitten Placeholder">
</div>

Solution
Let’s use our Angular directive to handle the above scenario. We can use error event of html img element when it fails to load.
In angular we can directly use (error) event on image tag or better — we can our directive to listen to such events. Let’s upgrade our directive.
//..............
constructor(private el: ElementRef) {
console.log(el.nativeElement);
}
@HostListener('error') onError () {
this.el.nativeElement.src = '../assets/placeholder.png'
}
//.............
The only that change here is the use of Host Listener. We are using host Listener to listen for error event and update the src attribute accordingly. I am linking to image in the assets folder. You can use the image of your choice and the result becomes

For the first attempt image fails to load (that can be seen on developer tools). Then our directive kicks and replaces the failed url with local replacement. Hence we see the placeholder image.
Now we can use this directive where ever we want in the entirety of our Angular App. There is one caveat though; If for some reason the image in asset folder failed to load than we are in the infinite loop. Because the onError function executes on every fail attempt. Which can simply be fixed with a flag variable. Our html remains the same but the final code listing fore the directive becomes.
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appImgFallback]'
})
export class ImgFallbackDirective {
alreadyTried: boolean = false;
constructor(private el: ElementRef) { }
@HostListener('error') onError(){
if (!this.alreadyTried) {
this.el.nativeElement.src = './../assets/placeholder.png'
}
this.alreadyTried = true
}
}