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.
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.