I have a clickaway listener as a directive that uses @HostListener
put on App.component.ts
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class AppComponent {
constructor(private clickaway: ClickawayService) {}
@HostListener("document:click", ["$event"]) documentClick(event: any): void {
this.clickaway.target.next(event.target);
}
}
@Directive({
selector: "[clickaway]",
})
export class ClickawayDirective implements OnInit, OnDestroy {
@Input() clickaway = null;
private subscription: Subscription = null;
constructor(
private clickawayService: ClickawayService,
private eleRef: ElementRef
) {}
ngOnInit() {
this.subscription = this.clickawayService.target.subscribe((target) => {
if (!this.eleRef.nativeElement.contains(target)) {
this.clickaway();
}
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
The ClickAway
service just provides a subject to listen to document:click
on the AppComponent
.
I am using this directive on a div that has child controlled by *ngIf, something like:
<div [clickaway]="doSomething">
<span *ngIf="isVisible">
<button (click)="closeSpan()">close</button> //closeSpan() sets isVisible to false.
</span>
</div>
The problem is whenever I click on close, it also triggers the clickaway's doSomething function.
I understand that *ngIf removes the span from dom thus when the directive runs !this.eleRef.nativeElement.contains(target)
evaluates to false, since element is not there.
What I have tried so far is:
closeSpan() {
setTimeout(() => this.isVisible = false, 100);
}
and moving the span out of view using position: absolute
and very high offsets and removing *ngIf
.
These solutions work, but I am seeking a more elegant way, preferably the directive itself handling such cases.
Thanks