24

I am struggling with Angular framework to get my application run smoothly, but I can't resolve an issue with routing. I have a top level AppComponent and app-routing.module.ts which manage the navigation via my custom SlideMenuComponent. My simplified html template of AppComponent:

<app-slide-menu [buttons]="menuButtons"></app-slide-menu>
<router-outlet></router-outlet>

My SlideMenuComponent has the following html as its core:

<nav><div *ngFor="let button of buttons">
    <a routerLink="{{button.routerLink}}"
    >{{button.name}}</a>
</div></nav>

A user can navigate to '/courses' through this slide menu, which is supervised by CoursesComponent that paginates links to a particular CourseComponents that are retrieved from the server. These components reside in their own courses.module.ts module whith their own courses-routing.module.ts. But when I click any of those links I get Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'? console warning, ngOnInit() is not called for openned CourseCompontent, and it doesn't update untill I click any of the buttons on the page. I had this issue when manually navigating via router.navigate() that was resolved by forwarding this task to NgZone.runTask(router.navigate()), but why does this happen with anchor tags and routerLink direcrives? Here is my CoursesComponent code excerpt:

<nav><ul>
        <li *ngFor="let course of api.data | paginate: {
            currentPage: currentPage, itemsPerPage: limit, totalItems: api.total
        }">
           <a
               [routerLink]="course._id"
               [queryParams]="{ page: '1', limit: '5' }"
           >
            {{course.name}} ({{course.description}})
           </a>
        </li>
</ul></nav>

A gif to demonstrate the issue:

enter image description here

Veetaha
  • 833
  • 1
  • 8
  • 19

5 Answers5

40

It seens a bug, try this as an workaround

constructor(private ngZone: NgZone, private router: Router) {}

public navigate(commands: any[]): void {
    this.ngZone.run(() => this.router.navigate(commands)).then();
}
calebeaires
  • 1,972
  • 1
  • 22
  • 34
  • worked like a charm, thanks. any problems to be expected by this solution? – Sevyls Nov 29 '18 at 15:46
  • Thanks calebeaires. It works also for my case. I my case, it happened while navigating into Angular7 route from angularJs in a Angular7 & AngularJs hybird upgrade app. – Rajez Jan 29 '19 at 05:37
  • 2
    This give me "ngZone is not defined" – Vincent Guyard Feb 06 '19 at 16:31
  • 1
    @VincentGuyard, add this to your imports: import { Component, OnInit, ..., NgZone } from '@angular/core'; – Vadim Jul 24 '19 at 17:27
  • 1
    There could be multiple comportment in the application, do i need to put given code in all those components ? – vndpal Aug 26 '19 at 13:20
  • For me I'm using FCM push notification in an Ionic chat app I built. When the user gets the notification they click the icon and the app opens but it doesn't take them to the specified page. I tried using this method to no avail. – D.Hodges Oct 30 '19 at 23:47
  • 4
    Workaround works but feels like a hack and I can't figure what cause it. – Matjaz Hirsman May 03 '20 at 22:34
6

We had encountered this bug when we were navigating somewhere in our solution.

In our sidebar component we use on-push change detection and we have had one this.ref.detectChanges() when routing occured (change of parameters), which somehow broke routing (possibly kicking parallel running resolvers out of ngZone or something similar). After changing this to the anyway preferred this.ref.markForCheck() this bug didn't appear again. I'm happy we didn't need a workaround, weird behavior though..

Hope this helps anyone having the same issue.

fberthel
  • 61
  • 1
  • 1
5

Since there is no accepted answer, and since I found, using Angular 8, the top ranked answer does not quite work, and needs a little more explanation, I have the following to add:

My specific issue was with ag-grid, but I suspect any place where a component is injected, within an *ngFor or otherwise, can potentially cause this problem. This is very close to the previous answer, but instead declares a string parameter into the navigate function and then calls navigateByUrl instead of navigate.

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

@Component({
  template: `<tag (click)="navigate(path)">clickable text</tag>`
})

constructor(private ngZone: NgZone, private router: Router) { }

public navigate(path: string): void {
  this.ngZone.run(() => this.router.navigateByUrl(path)).then();
}

Your mileage may vary, but this worked beautifully for me.

iGanja
  • 2,374
  • 2
  • 28
  • 32
  • I'm trying this in my Ionic 4 (Angular) app when opening a push notification but is not making any result, still throwing the same warning and keeps stuck in the main page, any idea?? – Hamlet Minjares Feb 18 '20 at 05:14
  • @HamletMinjares - No ideas. Are you sure you are following this pattern exactly? The (click) event must call the component's navigate function as shown; it cannot use routerLink any longer. Other than that, I would think this would work fine in Ionic. I can't see why that would make a difference, but I don't have a lot of experience with Ioninc. – iGanja Feb 20 '20 at 19:46
  • I apologize; I was using a guard class to validate before call this method and didn't noticed that this guard was redirecting without ng zone. Just after I correted that, it worked like a charm, thank you! – Hamlet Minjares Feb 21 '20 at 00:19
  • your answer helped me with my issue. despite using angular 10 instead of 8, it worked perfectly – tecnocrata Dec 13 '21 at 09:49
  • Same issue with me also seems to be related to ag-grid rendering a template. I may do this as a temporary workaround, but I'd really like to get it working consistently with [routerLink] because I'd like it to be a true link that can be opened in another tab if the user chooses to. – reads0520 Mar 11 '22 at 21:41
1

For me, this error was because of a part of the code running outside the NgZone. After which we had a this.ref.detectChanges() running to detect changes. The changes detected would change the DOM and put <a [routerLink]="..."></a>. This use to break the routing. Solution : We ran the part of code outside NgZone inside NgZone but using this.ngZone.run . This way we did not have to use ref.detectChanged() as the changed(RxJS Subject subscribe) were in NgZone. This solved the routing issue.

Hope this helps anyone having the same issue.

Omkar
  • 402
  • 1
  • 3
  • 10
0

Working Example - Refer this

 public backButtonSubscription;
      ngAfterViewInit(): void {
        let self = this;
        this.backButtonSubscription = this.platform.backButton.subscribe(() => {
          console.log("back preseed \n \n");
    
          self._ngZone.runOutsideAngular(() => {
            setTimeout(() => {
              self.Router.navigateByUrl("/stock-management");
            }, 100);
          });
        });
      }
Shashwat Gupta
  • 5,071
  • 41
  • 33