An example of a useful standalone directive

Alain Chautard
Angular Training
Published in
3 min readDec 13, 2023

--

Angular is all about components, but we need to remember that directives can be a better option occasionally.

To illustrate this, here is an example of a feature we want to implement:

  • Detect when the user clicks outside of an element/component
  • Send an event to the parent component when that happens

This feature can be helpful if you want to close a dropdown, a dialog, or a date picker when the user clicks away from it.

First, we generate a directive and inject a reference to its native HTML element:

@Directive({
selector: '[appClickOutside]',
standalone: true
})
export class ClickOutsideDirective {
// Inject the native HTML element so we can work with it
private element = inject(ElementRef).nativeElement;

}

Then, we want the directive to listen to all clicks on the webpage. We can do so using the following HostListener syntax:

// Listen to all clicks on the current document
// and pass the click event as a parameter to our method
@HostListener('document:click', ['$event'])
onClick(event: Event): void {
// We check if the target of the click is our current element or not
if (this.element != event.target) {
// If it's not, we want to emit an event here
}
}
Angular Certification Exam

At this point, we can detect clicks outside the element. The final step is to emit an event when that happens. We want the API of the directive to look like this when we use it on an element:

  <div (appClickOutside)="doSomethingWhenClickOutside()">
Click outside of this block to get a surprise
</div>

To achieve that behavior, a nifty trick is to name the @Output property just like the selector of our directive:

@Directive({
selector: '[appClickOutside]'
})
export class ClickOutsideDirective {
@Output() appClickOutside = new EventEmitter();

As you can see, the selector and the output are called appClickOutside.

Subscribe to the Daily Angular Newsletter!

Here is what the final code for our directive looks like, with all its imports:

import {Directive, ElementRef, EventEmitter, HostListener, inject, Output} 
from '@angular/core';

@Directive({
selector: '[appClickOutside]',
standalone: true
})
export class ClickOutsideDirective {
@Output() appClickOutside = new EventEmitter();

private element = inject(ElementRef).nativeElement;

@HostListener('document:click', ['$event'])
onClick(event: Event): void {
if (this.element != event.target) {
this.appClickOutside.emit();
}
}
}

And now, we have a fully reusable standalone directive that can be used on any component/HTML element in our application. You can access the entire code and run the example here on Stackblitz.

My name is Alain Chautard. I am a Google Developer Expert in Angular and a consultant and trainer at Angular Training, where I help web development teams learn and become comfortable with Angular.

If you need any help with web development, feel free to get in touch!

If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and the Daily Angular Newsletter for helpful daily tips.

--

--