Home > front end >  Prevent MouseEvent on Angular directive from triggering change detection
Prevent MouseEvent on Angular directive from triggering change detection

Time:07-30

I am working on an Angular project where a directive needs to react to mouseenter and mouseleave events. it needs to update the CSS of the host element where it is used.

Consider the contrived example below:

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appMouseEvents]',
})
export class MouseEventsDirective {
  constructor(private element: ElementRef) {

  }
  @HostListener('mouseEnter', ['$event'])
  onm ouseEnter() {
    console.log('mouseenter');
  }

  @HostListener('mouseleave',['$event'])
  onm ouseLeave() {
    console.log('mouseleave');
  }

}

app.component.html

<hello name="{{ name }}"></hello>
<p appMouseEvents>
  Hover Mouse over me
</p>

When I profile the application in Angular Dev tools, I see that these event cause Angular to trigger change detection which is not needed as only the CSS of host element is affected. enter image description here

Is there a way to react to mouse event in the directive and not trigger change detection?

Here's a demo application: https://stackblitz.com/edit/angular-ivy-r6ln5w?file=src/app/app.component.ts

CodePudding user response:

Yes, event fired on DOM would be monkey patched by zone.js and it triggers a change detection.

I would suggest you to go here using native browser feature. Like use :hover pseudo css class on host element.

:host:hover {
    // CSS rules
}

Alternative way to fix it would be using this.zone.runOutsideAngular as other suggested answer.

CodePudding user response:

You may need to use NgZone to run code outside of Angular zone (scope) and thus prevent angular change detection https://angular.io/api/core/NgZone

Your directive will look something like this:

import { Directive, ElementRef, HostListener, NgZone } from '@angular/core';

@Directive({
  selector: '[appMouseEvents]',
})
export class MouseEventsDirective {
  constructor(private element: ElementRef, private zone: NgZone) {
    this.element.nativeElement.addEventListener('mouseenter', (e) => {
      this.zone.runOutsideAngular(() => {
        e.preventDefault();
        // e.stopPropagation();
        console.log('mouseenter');
      });
    });

    this.element.nativeElement.addEventListener('mouseleave', (e) => {
      this.zone.runOutsideAngular(() => {
        e.preventDefault();
        // e.stopPropagation();
        console.log('mouseleave');
      });
    });
  }
}

Here is you stackblitz modified https://stackblitz.com/edit/angular-ivy-faavr3?file=src/app/mouse-events.directive.ts

Hope this helps.

  • Related