I upgraded my Angular from 4 to 6, and consequently had a problem with my click-off policy, it stopped working on all components.
my directive:
view:
<div #insideElement></div>
component:
export class SomeClass {
@ViewChild("insideElement") insideElement;
@HostListener('document:click', ['$event.target'])
public onClick(targetElement) {
const clickedInside = this.insideElement.nativeElement.contains(targetElement);
if (!clickedInside) {
console.log('outside clicked');
}
}
}
This is a modification of @YoungHyeong Ryu answer above, but with unsubscription, so that the handler stops working when the component is unmounted.
DEMO https://stackblitz.com/edit/angular-1q4pga
import { Component, Input, OnInit, OnDestroy, ViewChild } from '@angular/core';
@Component({
selector: 'app-click-outside',
template: `<div #insideElement>Click outside me.</div>`
})
export class ClickOutsideComponent implements OnInit, OnDestroy {
@ViewChild('insideElement', { static: false }) insideElement;
public ngOnInit() {
this.onDocumentClick = this.onDocumentClick.bind(this);
document.addEventListener('click', this.onDocumentClick);
}
public ngOnDestroy() {
document.removeEventListener('click', this.onDocumentClick);
}
protected onDocumentClick(event: MouseEvent) {
if (this.insideElement.nativeElement.contains(event.target)) {
return;
}
console.log('Clicked outside!');
}
}
Here, we remove the event listener on destroy.
Also, normally a method added by addEventListener is executed in a global context (instead of this
context); so we should take care of it and bind onDocumentClick
method to this
(we do it in ngOnInit
). Now we can use this
inside onDocumentClick
.
Run the inside NgZone
.
Example:
export class AppComponent {
opened: boolean = false;
constructor(private ngZone: NgZone) {
}
closeOutsideSidenav(e) {
this.ngZone.run(() => {
this.opened = !this.opened;
})
}
}
I added my code to stackblitz. https://stackblitz.com/edit/angular-gyhtym (click outside of the "Highlight Me!")
You're referencing "this" in your template, which is not necessary. I made a working example of that directive:
https://stackblitz.com/edit/angular-piqewb
And theres no reason to have the directive on the div twice.
<div id="sidenav" *ngIf="opened" class="sidenav" [ngClass]="getClasses()" [ngStyle]="getStyles()" (clickOutside)="closeOutsideSidenav()">
<header> {{ navTitle }} </header>
<i *ngIf="showCloseButton" class="iconic iconic-x-thin close-icon" (click)="closeSidenav()"></i>
<ng-content></ng-content>
</div>
<div *ngIf="backdrop && opened" class="sidenav-backdrop"></div>