Router Navigate does not call ngOnInit when same page
Asked Answered
A

16

149

I am calling router.navigate on same page with some query string parameters. In this case, ngOnInit() does not call. Is it by default or do I need to add anything else ?

Anurag answered 16/1, 2017 at 14:12 Comment(0)
D
195

You can inject the ActivatedRoute and subscribe to params

constructor(route:ActivatedRoute) {
  route.params.subscribe(val => {
    // put the code from `ngOnInit` here
  });
}

The router only destroys and recreates the component when it navigates to a different route. When only route params or query params are updated but the route is the same, the component won't be destroyed and recreated.

An alternative way to force the component to be recreated is to use a custom reuse strategy. See also Angular2 router 2.0.0 not reloading components when same url loaded with different parameters? (there doesn't seem to be much information available yet how to implement it)

Disposal answered 16/1, 2017 at 14:14 Comment(6)
this subscription is not working it wil only trigger on initDm
@OsandaWedamulla that's specific to your code then. Hard to tell without more details thoughMazzard
Don't you need to unsubscribe from this also or is this done automatically?Martsen
@Martsen as a rule of thumb you should explicitly unsubscribe in your cpde when you explicitly subscribed. If you have above code in your root component then unsubscribing is redundant because the lifetime of the root component is usually the same as the lifetime of the whole Angular app.Mazzard
This solution works when combined with https://mcmap.net/q/157411/-router-navigate-does-not-call-ngoninit-when-same-page (or setting onSameUrlNavigation globally).Copenhagen
@S.Roose it does not for me.. :(Eller
S
140

You could adjust the reuseStrategy on the Router.

constructor(private router: Router) {
    // override the route reuse strategy
    this.router.routeReuseStrategy.shouldReuseRoute = function() {
        return false;
    };
}
Solutrean answered 15/12, 2017 at 7:29 Comment(10)
This will initiate the ngInit of every component on the page.Madelaine
Well, @Pascal, you compelled me to log in to SO just to up-vote your answer. I would add that in Angular 6 you must also add onSameUrlNavigation: 'reload' to the config object like so: RouterModule.forRoot(appRoutes, {onSameUrlNavigation: 'reload'}). Coudn't speak for any other Angular versions, though.Lofty
@Hildy, I was compelled to do the same :) Thanks @Pascal! if you prefer lambda you can do this this.router.routeReuseStrategy.shouldReuseRoute = () => false;Denise
@Solutrean where should i add this entry?Worthless
@Worthless i created a routing.module.ts and placed the code as this constructorSolutrean
maybe this should be the accepted answer; putting ngOnInit items in a subscription is counter intuitivePoyssick
@Swaprks.. I have a similar issue and would like to try out your answer above. But it doesn't tell me much as a beginner in this field. Exactly where in the code am I supposed to place this code piece? Am I supposed to change anything in the code snippet (e.g. ´function()´ )? If you created a new file with the name routing.module.ts, how is it supposed to interact with other files then? There is also a file called app-routing.module.ts which is automatically created.Pediculosis
still useful... I tried to call ngOnIt in angular 8 after navigation and was not able to call the same. after using this ia m now able to call ngOnIt. not sure whether this is the right approach in angular 8 or not BUT for now its fixing my issue. +1 for that.Haeres
This worked for my case. My case is i have the url: /home/students/1 which has component showing information about that particular student with Id=1 and below a 2nd component that has a network graph with all of his siblings. In the nav bar, there's a search bar, i'm searching for a different student (let's say student with ID=2). Witth the proposed solution above, i'm forcing the creation of all the component (which the behavior that i want) and showing me all about student with ID = 2Appendant
It baffles me that /user/3 and /user/5 are treated as equal. But maybe I got something wrong.Perri
H
43

Angular 9

I have used the following and it worked.

onButtonClick() {
    this.router.routeReuseStrategy.shouldReuseRoute = function () {
        return false;
    }
    this.router.onSameUrlNavigation = 'reload';
    this.router.navigate('/myroute', { queryParams: { index: 1 } });
}
Haws answered 5/5, 2020 at 20:20 Comment(6)
In addition, also use lambda instead of above, this.router.routeReuseStrategy.shouldReuseRoute = () => false;Yugoslavia
Works in Angular 8.Merely
Would this change router behavior across the application or just trigger once for this specific navigate()?Enscroll
Works in Angular 9.Winfield
@Enscroll What I have observed is that after setting shouldReuseRoute, it is set for the entire app. I checked this by using console.log(this.router.routeReuseStrategy on page 1, setting the strategy to that function on page 2 and then navigating back to page 1. On initial load page 1 reports no function, but after navigating to page 2 and back the function still exists. If you only want to use this in 1 component, perhaps re-set the strategy in NgOnDestroy?Liking
Be careful with this!!! When that code executes it'll change the behavior of the router permanently. This means for example , if you use router.navigate() to the same url it'll trigger ngOnInit again.Crass
Q
11

Do you probably need reloading page? This is my solution: I've changed the @NgModule (in app-routing.module.ts file in my case) :

@NgModule({
  imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})] })
Quadrangle answered 26/9, 2018 at 7:35 Comment(3)
What is the 'routes' over here.Yugoslavia
@DhwanilPatel your Angular app routes. For example "const routes: Routes = [ { path: 'crisis-center', component: CrisisListComponent }, { path: 'heroes', component: HeroListComponent }, ]" angular.io/guide/router#register-router-and-routesQuadrangle
onSameUrlNavigation: 'reload' will only reload the Guards but not the componentThigmotropism
Y
8

On your navigation method,

this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate(['/document'], {queryParams: {"search": currentSearch}});
Yugoslavia answered 26/5, 2020 at 7:12 Comment(3)
this destroy the rest of the navigation modelIgraine
Be careful with this!!! When that code executes it'll change the behavior of the router permanently. This means for example , if you use router.navigate() to the same url it'll trigger ngOnInit again.Crass
Yes, Developer have to manage relevant things accordingly.Yugoslavia
F
5

Here is a collection of the best ideas on this page with more information

Solution 1 - Use params subscription:

Tutorial: https://angular-2-training-book.rangle.io/routing/routeparams#reading-route-parameters

Docs: https://angular.io/api/router/ActivatedRoute#params

In each of your routing components that use param variables include the following:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';

// ...

@Component({
    // ...
})
export class MyComponent implements OnInit, OnDestroy {
    paramsSub: Subscription;

    // ...

    constructor(activeRoute: ActivatedRoute) {

    }

    public ngOnInit(): void {
        // ...
        this.paramsSub = this.activeRoute.params.subscribe(val => {
            // Handle param values here
        });

        // ...
    }

    // ...

    public ngOnDestroy(): void {
        // Prevent memory leaks
        this.paramsSub.unsubscribe();
    }
}

Some common issues with this code is that subscriptions are asynchronous and can be trickier to deal with. Also you can't forget to unsubscribe on ngOnDestroy or else bad things can happen.

Good thing is that this is the most documented and common way to handle this problem. There's also a performance improvement doing it this way since you are reusing the template instead of destroying and recreating each time you visit a page.

Solution 2 - shouldReuseRoute / onSameUrlNavigation:

Docs: https://angular.io/api/router/ExtraOptions#onSameUrlNavigation

Docs: https://angular.io/api/router/RouteReuseStrategy#shouldReuseRoute

Docs: https://angular.io/api/router/ActivatedRouteSnapshot#params

Find where RouterModule.forRoot is located in your project (normally found in app-routing.module.ts or app.module.ts):

const routes: Routes = [
   // ...
];

// ...

@NgModule({
    imports: [RouterModule.forRoot(routes, {
        onSameUrlNavigation: 'reload'
    })],
    exports: [RouterModule]
})

Then in AppComponent add the following:

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

// ...
@Component({
    // ...
})
export class AppComponent implements OnInit {
    constructor(private router: Router) {
    }

    ngOnInit() {
        // Allows for ngOnInit to be called on routing to the same routing Component since we will never reuse a route
        this.router.routeReuseStrategy.shouldReuseRoute = function() {
            return false;
        };

        // ...
    }

    // ...
}

Last, in your routing components you can now handle param variables like this:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

// ...

@Component({
    // ...
})
export class MyComponent implements OnInit {
    // ...

    constructor(activeRoute: ActivatedRoute) {

    }

    public ngOnInit(): void {
        // Handle params
        const params = +this.activeRoute.snapshot.params;

        // ...
    }

    // ...
}

Common issues with this solution is that it isn't common. Also you are changing the default behavior of the Angular framework, so you can run into issues people wouldn't normally run into.

Good thing is that all your code is synchronous and easier to understand.

Fiasco answered 10/4, 2020 at 6:27 Comment(0)
I
3

The solution could be subscribing to your router events.

Import Router and NavigationEnd

import { Router, NavigationEnd } from '@angular/router';

Initiate router in the constructor.

constructor(router: Router) {

Subscribe to router events in the constructor.

        this.router.events.subscribe((ev) => {
          if (ev instanceof NavigationEnd) {
            //do something
          }
        });

I found out developers sometimes need generic code for every router.navigate, this can be the answer if it's in app.component.ts or in any shared component.

Interventionist answered 7/7, 2022 at 1:49 Comment(0)
O
1

NgOnInit would be called once when an instance is created. For the same instance NgOnInit won't be called again. In order to call it it is necessary to destroy the created instance.

Overdue answered 16/1, 2017 at 14:19 Comment(0)
D
1

I've had the same issue, additionally I got the warning:

did you forget to call `ngZone.run()`

This site provided the best solution:

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

...

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

  redirect(to) {
    // call with ngZone, so that ngOnOnit of component is called
    this.ngZone.run(()=>this._router.navigate([to]));
  }
Durant answered 9/7, 2020 at 9:50 Comment(1)
With respect to this, I wish to know how to handle the issue when you simply refresh the page with the new route. In that case, the NgZone warning will still be present.Aunt
H
0

This problem is likely coming from that fact that you are not terminating your subscriptions using ngOnDestroy. Here is how to get'ter done.

  1. Bring in the following rxjs subscription import. import { Subscription } from 'rxjs/Subscription';

  2. Add OnDestory to your Angular Core Import. import { Component, OnDestroy, OnInit } from '@angular/core';

  3. Add OnDestory to your export class. export class DisplayComponent implements OnInit, OnDestroy {

  4. Create a object property with a value of Subscription from rxjs under your export class for each subscription on the component. myVariable: Subscription;

  5. Set the value of your subscription to MyVariable: Subscriptions. this.myVariable = this.rmanagerService.getRPDoc(books[i].books.id).subscribe(value => {});

  6. Then right below ngOninit place the ngOnDestory() life cycle hook and put in your unsubscribe statement for your subscription. If you have multiple, add more ngOnDestroy() { this.myVariable.unsubscribe(); }

Heterogenous answered 13/5, 2018 at 17:44 Comment(1)
I keep typing ngOnDestory instead of ngOnDestroy myself too ;-)Wheedle
S
0

Create a different path for the same component in the routes array .

const routes : Routes = [ { path : "app", component: MyComponent }, { path: "app-reload", component: MyComponent }];

If the current URL is "app" then navigate using "app-reload" and vice versa.

Synclastic answered 28/11, 2019 at 15:11 Comment(0)
F
0
const defaultStrategy = this.router.routeReuseStrategy.shouldReuseRoute;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.navigate(['path/to']).finally(
() => {this.router.routeReuseStrategy.shouldReuseRoute = defaultStrategy;})
Fixture answered 2/6, 2023 at 21:6 Comment(1)
Although this code might answer the question, I recommend that you also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes.Carner
S
-1

Consider moving the code you had in ngOnInit into ngAfterViewInit. The latter seems to be called on router navigation and should help you in this case.

Sporule answered 15/3, 2017 at 21:5 Comment(1)
that doesnt really happenIntertype
V
-1

This is the best solution for Router Navigate does not call the ngOnInit function when same page

// override the route reuse strategy
this.router.routeReuseStrategy.shouldReuseRoute = function() {
    return false;
};
Vocal answered 25/1, 2022 at 12:16 Comment(0)
C
-1
// this code is for redirecting to the dashboard page with calling ngOnInIt
    this.router.routeReuseStrategy.shouldReuseRoute = () => false;
    this.router.onSameUrlNavigation = 'reload';
    this.router.navigate(['./dashboard']);

this should works and should only used for special cases. When you dont have to navigate much after redirection you can use this.otherwise this will messed up some times. Note that you can always make the 'routeReuseStrategy' to true when you needed.

Chanda answered 29/1, 2022 at 7:3 Comment(0)
L
-9

When you want to router navigate on the same page and want to call ngOnInit(), so you do like that e.g,

this.router.navigate(['category/list', category]) .then(() => window.location.reload());

Lofton answered 4/4, 2019 at 10:46 Comment(1)
That breaks all SPA logicSlug

© 2022 - 2024 — McMap. All rights reserved.