Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?
Asked Answered
D

7

128

I am using Angular 7 and facing an issue => after login the API GET calls successfully and the component receiving data too, but UI is not displaying that data.

When I open the browser console, immediately the data gets populated on the UI and a warning is showing in the console.

"core.js:15686 Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?"

I have googled this warning and found some workaround like this.ngZone.run() and call my API's inside it.

But the issue is, I am using more than 40 components and calling so many API in each component. So I have to call ngZone.run() on each API call, which seems to be difficult to do.

What's the better approach to overcome this issue?

app.component.ts

getEmployees(): void {
    this.employeeService.getEmployees().subscribe(e => {
        this.employees = e;
    });
}

app.service.ts

@Injectable()
export class EmployeeService {
    constructor(private httpClient: HttpClient) { }

    getEmployees() {
        return this.httpClient.get<EmployeeModel[]>('employees');
    }
Dingo answered 6/12, 2018 at 6:7 Comment(8)
Share your related component/service code, please.Weil
@ Ravinder Payal: Shared component/serviceDingo
It's not solving the purpose. please share the bootstrapping code for getEmployees. From where are you calling getEmployees(): voidWeil
Calling it from the ngOnInit() method in the component.Dingo
I am getting this error message as well. I'll open another posting to see if my case is more clear.Epigastrium
the snippets don't show usage of routing, which is the part that would cause the provided message. An http call in itself won't cause it. Anything interesting happens with employees when set in app.component.ts?Autodidact
how is the getEmployees() get called?Is that called directly by some click, or it is wrapped inside some callbacks?Disproof
if you get this when unit testing check out - github.com/angular/angular/issues/25837Tollmann
D
125

Usually this happens when you are wrapping angular calls inside some external js callback, from external JavaScript not related to angular code.

Example app.component.ts:

callMyCustomJsLibrary() {
  googleSdk.getLocations(location => this.getEmployees());
}

getEmployees(): void {
    this.employeeService.getEmployees().subscribe(e => {
        this.employees = e;
    });
}

In this case you will have to include the call into the NgZone, example: this.ngZone.run(() => this.getEmployees());

The app.component.ts would then look like the following:

callMyCustomJsLibrary() {
  googleSdk.getLocations(location => this.ngZone.run(() => this.getEmployees()));
}

getEmployees(): void {
    this.employeeService.getEmployees().subscribe(e => {
        this.employees = e;
    });
}
Disproof answered 10/3, 2019 at 11:52 Comment(4)
In my case it happens in rxjs subscription created by angular component.Prefabricate
Thank you very much, I was hanging with razorpay integration callback, nothing was working.Aquitaine
It's missing the addition of private zone: NgZone in the constructor, but nice, thanks!Weldon
@PrinceKumarSharma did you solve the issue with razorpay? I am having the same issue and I see it be because of the background div that covers up the entire screen when the pop-up opens is left behind when the pop-up is manually closed. How did you take that off?Cost
C
67

My issue fixed by wrapping the router navigation command in ngZone. Please check the code below

in constructor add "private zone: NgZone".

private zone: NgZone

this.zone.run(() => {
                    this.router.navigate(['/login']);
                });
Cocke answered 7/10, 2020 at 20:15 Comment(1)
It works with me using Angular v17Beslobber
G
16

If I talk from top then the meaning of this warning is that the event is triggered outside of zone. Angular uses zones, also called execution contexts for change detection and UI rendering. So while navigating, Angular expects to re-renderer the UI but it is not happening here. That's the problem you are facing. So it throws this warning because Angular is processing the change detection and UI rendering when the code executed lies inside of Angular zone.

But actually, this warning comes when you are trying to call the router navigation inside the subscribe method.

If you put your router navigation call outside of subscribe method then it will not throw this warning again and you will see the expected UI after navigation.

For more information about zones, read and watch the videos mentioned below:

https://blog.thoughtram.io/angular/2017/02/21/using-zones-in-angular-for-better-performance.html#running-outside-angulars-zone

https://www.youtube.com/watch?v=3IqtmUscE_U&t=150

Gheber answered 21/3, 2019 at 16:47 Comment(2)
I have a router.navigate() call inside a subscribe() method block. When you say to put it outside, where exactly would that be ?Leodora
For me, the upgrade to Angular 8 made the existing router call inside the error handler trigger a 'Navigation triggered outside Angular zone' error. I had to remove the router.navigate() call in the custom error handler block errorService.log(error).subscribe(errorWithContextInfo => {Leodora
P
13

I had the same problem. This worked for me, you have two ways:

First form:

document.location.href=`/component/${param}/...`

The problem with this is that it will reload the entire application and this will make it slower

Second method:

Import this

import { Router } from '@angular/router';
import { Component, NgZone } from '@angular/core';

constructor:

 constructor(private _ngZone: NgZone, private _router: Router) { }


  goTo(comp, param){
    this._ngZone.run(()=>{
    this._router.navigate([comp, param])
    });
  }

So when you want to go to your component, simply:

this.goTo("/myComponent", "my-param-value");

I hope it works for you

Penicillate answered 2/3, 2021 at 23:23 Comment(0)
T
8

In my case the issue was triggered by a test that was calling the router in order to make sure the tested component reacts correctly to route changes. The code in my test roughly looked like this:

const router: Router = TestBed.inject(Router);
await router.navigateByUrl('some-route');

Injecting NgZone and wrapping the router call was the solution

const router: Router = TestBed.inject(Router);
const ngZone: NgZone = TestBed.inject(NgZone);
await ngZone.run(async () => router.navigateByUrl('some-route'));

This is also needed if you directly test a component method that triggers a navigation:

const ngZone: NgZone = ngZone = TestBed.inject(NgZone);
ngZone.run(() => component.someMethod()); // someMethod() triggers a router navigation
Timbrel answered 28/9, 2022 at 12:26 Comment(0)
F
0

Working Solution - FIX

 ngAfterViewInit(): void {
    let self = this;
      this.platform.backButton.subscribe(() => {
      console.log("back preseed \n \n");

      self._ngZone.runOutsideAngular(() => {
        setTimeout(() => {
          self.Router.navigateByUrl("/stock-management");
        }, 100);
      });
    });
  }
Flickinger answered 16/7, 2021 at 20:50 Comment(0)
L
-9

This happens if you try injecting a custom made service:

constructor(private myService: MyService) {
}

while you forgot to provide it in the module configuration:

@NgModule({
  providers: [
    MyService
  ]
Leodora answered 17/4, 2019 at 7:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.