Search code examples
angularangular5angular2-directivesangular-directive

Angular 5 custom directive fires for element wrapped into *ngIf=false


I'm working on directive that should close dropdown after click outside. The element that has directive also has*ngIf and is not present on the page at the time of click, but directive is still firing the event - so the dropdown is never opened as showList becomes false immediatelly after (click)="showList = true" fired. How do I avoid this?

stackblitz

import { Directive, HostListener, ElementRef, EventEmitter, Inject, PLATFORM_ID, Output, Input, OnInit, Renderer2, AfterContentInit } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Directive({
  selector: '[clickOutside]'
})
export class ClickOutsideDirective implements AfterContentInit {

  ngAfterContentInit(): void {
    this.contentInitialized = true;
  }
  @Input("clickOutside") config: any = {};
  @Output() clickOutside: EventEmitter<any> = new EventEmitter();
  private isBrowser: boolean = false;
  private isIE11: boolean = false;
  private contentInitialized: boolean = false;

  constructor(private renderer: Renderer2, private _el: ElementRef, @Inject(PLATFORM_ID) private platformId: any) {
    this.isBrowser = isPlatformBrowser(platformId);
    this.isIE11 = this.isBrowser && !!(window as any).MSInputMethodContext && !!(document as any).documentMode;
  }

  @HostListener('document:click', ['$event'])
  public onClick(event) {
    console.log("CLICKED");
    if (this.contentInitialized && !(this._el.nativeElement.contains(event.target) || (event.target == window.document.querySelector(this.config.excludeLocator)))) {
      this.clickOutside.emit(event);
    }
  }

  @HostListener('document:keyup', ['$event'])
  private onKeyup(event) {
    if (event.keyCode == 27) {
      this.clickOutside.emit(event);
    }
  }

}
<div class="menu-trigger" (click)="showList = true"></div>
<ul class="list" *ngIf="showList" (clickOutside)="showGiftList = false">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  <li>item 4</li>
</ul>


Solution

  • To avoid Parent event running you need to stop it from child element, you can do it by

    $event.stopPropagation();
    

    The event.stopPropagation() method stops the bubbling of an event to parent elements, preventing any parent event handlers from being executed.

    If you want to learn some basics and advance more HTML EVENTS you can check this out.