Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 3291

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 61k
Author
Asked: November 26, 20242024-11-26T01:23:09+00:00 2024-11-26T01:23:09+00:00

Adding Event Listeners outside of the NgZone

  • 61k

If we're familiar with the Angular framework, we'll know that by default, any asynchronous event triggers the change detection process. In certain situations, we don't even have to worry about it; it just works as expected. However, in some cases, running the change detection process too frequently can lead to poor runtime efficiency.

Execution of code in the NgZone

Assume that we want to call the console.log method when we click the button:

Click handler in NgZone

<button>Click me!</button> import { AfterViewChecked, Component } from "@angular/core"; @Component({ selector: "my-app", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"] }) export class AppComponent implements AfterViewChecked { onClick() { console.log("onClick"); } ngAfterViewChecked() { console.log("CD performed"); } }  
Enter fullscreen mode Exit fullscreen mode

When we click the button, both the bound event listener and the change detection process are triggered. In a real-world scenario, instead of calling console.log, we could perform an action that does not require bindings to be updated.

Incorrect usage of the runOutsideAngular method

Although the method in question allows us to opt-out of the change detection process, it must provide code to register an event listener. As a result, the following solution, which simply runs the callback outside of the NgZone, will not prevent the change detection process from being performed:

 import { AfterViewChecked, Component, NgZone } from "@angular/core"; @Component({ selector: "my-app", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"] }) export class AppComponent implements AfterViewChecked { constructor(private readonly zone: NgZone) {} onClick() { this.zone.runOutsideAngular(() => { console.log("onClick"); }); } ngAfterViewChecked() { console.log("CD performed"); } }  
Enter fullscreen mode Exit fullscreen mode

Execution of code outside of the NgZone – using ViewChild

We may use the ViewChild decorator to get a reference to the DOM node and add an event listener in one of the following ways:

Read More: Introduction To Angular Cli Builders

Click handler outside NgZone

<button>Click me!</button>import { AfterViewChecked, AfterViewInit, Component, ElementRef, NgZone, Renderer2, ViewChild } from "@angular/core"; import { fromEvent } from "rxjs"; @Component({ selector: "my-app", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"] }) export class AppComponent implements AfterViewInit, AfterViewChecked { @ViewChild("btn") btnEl: ElementRef; constructor( private readonly zone: NgZone, private readonly renderer: Renderer2 ) {} onClick() { console.log("onClick"); } ngAfterViewInit() { this.setupClickListener(); } ngAfterViewChecked() { console.log("CD performed"); } private setupClickListener() { this.zone.runOutsideAngular(() => { this.setupClickListenerViaNativeAPI(); // this.setupClickListenerViaRenderer(); // this.setupClickListenerViaRxJS(); }); } private setupClickListenerViaNativeAPI() { this.btnEl.nativeElement.addEventListener("click", () => { console.log("onClick"); }); } private setupClickListenerViaRenderer() { this.renderer.listen(this.btnEl.nativeElement, "click", () => { console.log("onClick"); }); }  
Enter fullscreen mode Exit fullscreen mode

Execution of code outside of the NgZone – using directive

While the previous paragraph's solution works well, it is a little verbose. we can encapsulate the logic in an attribute directive, which allows dependency injection to provide easy access to the underlying DOM element (ElementRef token). Then, outside of the NgZone, we can add an event listener and emit an event when it's appropriate:

Click handler outside NgZone

<button>Click me!</button>import { Directive, ElementRef, EventEmitter, NgZone, OnDestroy, OnInit, Output, Renderer2 } from "@angular/core"; @Directive({ selector: "[click.zoneless]" }) export class ClickZonelessDirective implements OnInit, OnDestroy { @Output("click.zoneless") clickZoneless = new EventEmitter(); private teardownLogicFn: Function; constructor( private readonly zone: NgZone, private readonly el: ElementRef, private readonly renderer: Renderer2 ) {} ngOnInit() { this.zone.runOutsideAngular(() => { this.setupClickListener(); }); } ngOnDestroy() { this.teardownLogicFn(); } private setupClickListener() { this.teardownLogicFn = this.renderer.listen( this.el.nativeElement, "click", (event: MouseEvent) => { this.clickZoneless.emit(event); } ); } } 
Enter fullscreen mode Exit fullscreen mode

Execution of code outside of the NgZone – using Event Manager Plugin

The directive-based approach has the disadvantage of not being able to be configured for an event type. Thankfully, Angular allows us to build our Event Manager Plugin. In other words, we take control of adding a listener for an event whose name corresponds to the predicate function (the supports method). If a match is found, the addEventListener method is called, allowing us to handle the job. The two methods are part of the user-defined service that is registered as an EVENT MANAGER PLUGINS token provider:

Click handler outside NgZone

<button class="btn btn-primary"> Click me!</button> import { Injectable } from "@angular/core"; import { EventManager } from "@angular/platform-browser"; @Injectable() export class ZonelessEventPluginService { manager: EventManager; supports(eventName: string): boolean { return eventName.endsWith(".zoneless"); } addEventListener( element: HTMLElement, eventName: string, originalHandler: EventListener ): Function { const [nativeEventName] = eventName.split("."); this.manager.getZone().runOutsideAngular(() => { element.addEventListener(nativeEventName, originalHandler); }); return () => element.removeEventListener(nativeEventName, originalHandler); } } import { NgModule } from "@angular/core"; import { BrowserModule, EVENT_MANAGER_PLUGINS } from "@angular/platform-browser"; import { AppComponent } from "./app.component"; import { ClickZonelessDirective } from "./click-zoneless.directive"; import { ZonelessEventPluginService } from "./zoneless-event-plugin.service"; @NgModule({ imports: [BrowserModule], declarations: [ AppComponent, // ClickZonelessDirective ], bootstrap: [AppComponent], providers: [ { provide: EVENT_MANAGER_PLUGINS, useClass: ZonelessEventPluginService, multi: true } ] }) export class AppModule {}  
Enter fullscreen mode Exit fullscreen mode

Fortunately, by calling the initialization code from outside the NgZone, we can avoid triggering the change detection process:

3rd party lib initialized outside NgZone

 <button>Hover me!</button>import { Directive, ElementRef, NgZone, OnInit } from "@angular/core"; import tippy from "tippy.js"; @Directive({ selector: "[appTooltip]" }) export class TooltipDirective implements OnInit { constructor(private readonly zone: NgZone, private readonly el: ElementRef) {} ngOnInit() { this.zone.runOutsideAngular(() => { this.setupTooltip(); }); } private setupTooltip() { tippy(this.el.nativeElement, { content: "Bazinga!" }); } }  
Enter fullscreen mode Exit fullscreen mode

Conclusion

If we find ourselves in a situation where we are performing a task that does not need binding updates in response to a DOM event, we can improve the performance of our application by not triggering an unwanted change detection run Outside of the NgZone, we must be careful when registering an event listener. The most elegant and reusable solution is to use a custom Event Planner Plugin. If we're using a third-party solution that modifies the DOM, we should think about running its initialization code outside of the NgZone.

angularbeginnersngzonewebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    How to ensure that all the routes on my Symfony ...

    • 0 Answers
  • Author

    Insights into Forms in Flask

    • 0 Answers
  • Author

    Kick Start Your Next Project With Holo Theme

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.