In Angular, directives are defined as classes that can add new behavior to the elements in the template or modify existing behavior.

The purpose of directives in Angular is to maneuver the DOM (Document Object Model),whether by adding new elements to the DOM or removing elements and even changing the appearance of the DOM elements.

There are three types of directives:

  • Attribute directives : enable Angular developers to define an attribute that, when added to an element change the appearance of the element or enhances its functionality.For example, ngStyle( applying styles) or ngClass(applying CSS classes).
  •  Structural directives: enable Angular developers to add, edit, and remove elements in the DOM. An example would be *ngIf(adding or removing element from DOM) or *ngFor(lists elements of every iteration).
  • Component directives: this type of directive has a template or template URLs. In effect, it is a component directive that shows something in DOM.

Sometimes, in Angular, we have a situation where we need to detect the outside click of an element inside our Web application. For example, close some dialog when we click outside the dialog container on the user interface.

For this kind of purposes, we can create a specific attribute directive.

This directive will use only Angular core stuff like directives, output, host listener decorators, and event emitters.

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

@Directive({
  selector: '[appClickOutside]'
})
export class ClickOutsideDirective {

  @Output() 
  appClickOutside: EventEmitter<void> = new EventEmitter();

  @HostListener('document:click', ['$event']) 
  onDocumentClick(event: PointerEvent) {
    const nativeElement: any = this.elementRef.nativeElement;
    const clickedInside: boolean = nativeElement.contains(event.target);
    if (!clickedInside) {
      this.appClickOutside.emit();
    }
  }

  constructor(private elementRef: ElementRef) { }

}

So, we have a class marked with a directive decorator. Notice that we have the same name for the directive selector and the directive output event name. In this way, we can use directives on the template as one line later.

The host listener is the function decorator responsible for executing a specific event handler with input parameters for a specific DOM event.

So, we are listening here for a click event on document level, and we are passing the whole event object (the pointer event object) to the document click handler.

Inside the click handler, we are checking if the event target element is not present inside the native element (the element where the directive is added), and only in that case are we using the click event emitter to emit the event without any parameters.

And now we can use the directive inside a simple component with an Angular logo image:

import { Component, OnInit } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';

@Component({
  selector: 'app-click-outside-test',
  template: `
    <ng-container>
    <img
      (appClickOutside)="onOutsideImgClick()"
      width="40"
      alt="Angular Logo"
      [src]="ngLogoImgSrc"
    />
    </ng-container>
  `
})
export class ClickOutsideTestComponent {

  ngLogoImgSrc: SafeResourceUrl;
  ngLogoBase64: string = `
    data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwM
    C9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEi
    IGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQ
    uMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLj
    ItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZ
    mlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJo
    NDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQ
    wLjl6IiAvPgogIDwvc3ZnPg==
  `;

  constructor(private domSanitizer: DomSanitizer) {
    this.ngLogoImgSrc = this
      .domSanitizer
      .bypassSecurityTrustResourceUrl(this.ngLogoBase64);
  }

  onOutsideImgClick() {
    console.log('Clicked outside img element');
  }
}

Now we are rendering the test component inside the app component responsible for bootstrapping the whole application:

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

@Component({
  selector: 'app-root',
  template: `
    <div class="toolbar" role="banner">
      <app-click-outside-test></app-click-outside-test>
      <span>Welcome</span>
    </div>
    <router-outlet></router-outlet>
  `,
  styles: [`
    :host {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
      font-size: 14px;
      color: #333;
      box-sizing: border-box;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }

    .toolbar {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      height: 60px;
      display: flex;
      align-items: center;
      background-color: #1976d2;
      color: white;
      font-weight: 600;
    }

    .toolbar img {
      margin: 0 16px;
    }
  `]
})
export class AppComponent {
  title = 'ng16';
}
Reference:

https://levelup.gitconnected.com/

By Shabazz

Software Engineer, MCSD, Web developer & Angular specialist

Leave a Reply

Your email address will not be published. Required fields are marked *