import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { bodyClick$ } from '../constants/event-streams.constants'; @Directive({ selector: '[appClickOutside],[appClickOutsideStream]' }) export class ClickOutsideDirective implements OnInit, OnDestroy { destroy$ = new Subject(); @Input() appClickOutsideStream = new Subject(); @Output() appClickOutside = new EventEmitter(); constructor (private elementRef: ElementRef) {} ngOnDestroy () { this.destroy$.next(); } ngOnInit () { setTimeout(() => { bodyClick$ .pipe( takeUntil(this.destroy$) ) .subscribe((e: MouseEvent) => { const targetElement = e.target; const clickedInside = this.elementRef.nativeElement .contains(targetElement); // We consider true click outside if clicked element still exist in a DOM, // but is not inside directive native element. Ignore clicks by 'close' buttons and // other disappearing elements. if (window.document.body.contains(targetElement) && !clickedInside) { this.appClickOutsideStream.next(e); this.appClickOutside.emit(e); } }); }); } }