EDIT: found out what was wrong; read the solution below.
I wrote a directive to open the link in a new tab, and it's working wonderfully.
The problem is when I put it in a table that has a link in the entire row, and an event listener in a specific cell that should not activate the link and call a function, but it captures the event and activates the link even though I called $event.stopPropagation
in said function.
In this example, clicking in the first row doesn't trigger routerLink
, but the second triggers routing:
<table>
<tr [routerLink]="'other'">
<td><p (click)="$event.stopPropagation()">Hello</p></td>
</tr>
<tr [appNewTab]="'other'">
<td><p (click)="$event.stopPropagation()">Hello</p></td>
</tr>
</table>
<router-outlet></router-outlet>
Directive code:
import { Directive, HostListener, Input } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
@Directive({
selector: '[appNewTab]',
})
export class NewTabDirective {
@Input() appNewTab = '/';
constructor(private router: Router, private route: ActivatedRoute) {}
private get url(): string {
return window.location.href + '/' + this.appNewTab;
}
@HostListener('mousedown', ['$event'])
onClick($event: MouseEvent) {
if ($event.ctrlKey || $event.metaKey || $event.button === 1) {
window.open(this.url, '_blank');
} else if ($event.button === 0) {
this.router.navigate([this.appNewTab], { relativeTo: this.route });
}
}
}
Run in stackblitz (updated with working example)
ps.: I'm also using $event.preventDefault()
, but I don't think it matters in this specific case.
ps2.: I put both $event.preventDefault()
and $event.stopPropagation()
inside the directive's onClick
method, but I don't think it matters too.
SOLUTION
The issue was that I was listening to the mousedown
event, but the click
event is different, so calling stopPropagation
inside it wouldn't stop the propagation of the click
event after the user released the mouse button.
I changed it from click
to mousedown
because the click
event only fires with the left button mouse, and I needed to listen to the middle mouse button clicks.
The solution was to separate the HostListener
in two:
import { Directive, HostListener, Input } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
@Directive({
selector: '[appNewTab]',
})
export class NewTabDirective {
@Input() appNewTab = '/';
constructor(private router: Router, private route: ActivatedRoute) {}
private get url(): string {
return window.location.href + '/' + this.appNewTab;
}
@HostListener('click', ['$event'])
onClick($event: MouseEvent) {
$event.preventDefault();
$event.stopPropagation();
if ($event.ctrlKey || $event.metaKey) {
window.open(this.url, '_blank');
} else {
this.router.navigate([this.appNewTab], { relativeTo: this.route });
}
}
@HostListener('mousedown', ['$event'])
onMouseDown($event: MouseEvent) {
$event.preventDefault();
$event.stopPropagation();
if ($event.button === 1) {
window.open(this.url, '_blank');
}
}
}