1

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');
    }
  }
}

0 Answers0