Search code examples
angularangular-router

Angular router.navigate is not changing url, but only on first load


In my component I have something which is basically:

public ngOnInit(): void {
    this._user.session$.subscribe((session: UserSession | null) => {
        if (session?.appKey) {
            // trying this as it was recommended on other posts
            this._router.onSameUrlNavigation = 'reload';
    
            this._router.navigate([this._returnUrl]).then(() => {
                if (this._router.url !== this._returnUrl)) {
                    // this should never happen
                    // only happens on first loading of app
                    window.location.reload();
                }
            });
        }
    })
}

On the first load, the this._router.navigate acts like it worked, but does not navigate away. If I reload, it will work - any idea what I can do so I don't have to do a manual reload?

angular/router 13.3.11


Solution

  • You can use routerReuseStrategy which recreates the route when we have the property reloadSamePath set as true in the data property of the current route.

    First add the property on the route.

    export const routes = [
      {
        path: 'test/:id',
        component: ChildComponent,
        data: { reloadSamePath: true },
      },
    ];
    

    Then in the reuse strategy, check if same route and return true to reload!

      shouldReuseRoute(
        future: ActivatedRouteSnapshot,
        curr: ActivatedRouteSnapshot
      ): boolean {
        if (future?.data?.['reloadSamePath'] && curr?.data?.['reloadSamePath']) {
          return true;
        }
        return false;
      }
    

    RouteReuseStrategy

    import {
      ActivatedRouteSnapshot,
      DetachedRouteHandle,
      RouteReuseStrategy,
    } from '@angular/router';
    
    export class CustomReuseStrategy implements RouteReuseStrategy {
      /**
       * Whether the given route should detach for later reuse.
       * Always returns false for `BaseRouteReuseStrategy`.
       * */
      shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return false;
      }
    
      /**
       * A no-op; the route is never stored since this strategy never detaches routes for later re-use.
       */
      store(
        route: ActivatedRouteSnapshot,
        detachedTree: DetachedRouteHandle
      ): void {}
    
      /** Returns `false`, meaning the route (and its subtree) is never reattached */
      shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return false;
      }
    
      /** Returns `null` because this strategy does not store routes for later re-use. */
      retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
        return null;
      }
    
      /**
       * Determines if a route should be reused.
       * This strategy returns `true` when the future route config and current route config are
       * identical.
       */
      shouldReuseRoute(
        future: ActivatedRouteSnapshot,
        curr: ActivatedRouteSnapshot
      ): boolean {
        if (future?.data?.['reloadSamePath'] && curr?.data?.['reloadSamePath']) {
          return true;
        }
        return false;
      }
    }
    

    Full Code:

    import { Component } from '@angular/core';
    import {
      provideRouter,
      RouterModule,
      Router,
      RouteReuseStrategy,
    } from '@angular/router';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { CustomReuseStrategy } from './router-reuse-strategy';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [RouterModule],
      template: `
        <router-outlet/>
      `,
    })
    export class ChildComponent {
      name = 'Angular';
    
      ngOnInit() {
        console.log('reloading');
      }
    }
    
    export const routes = [
      {
        path: 'test/:id',
        component: ChildComponent,
        data: { reloadSamePath: true },
      },
    ];
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [RouterModule],
      template: `
      <a (click)="navigateToSomeRoute()">some route</a>
        <router-outlet/>
      `,
    })
    export class App {
      name = 'Angular';
    
      constructor(private router: Router) {}
    
      navigateToSomeRoute() {
        this.router.navigate(['test', `${Math.round(Math.random() * 100)}`]);
      }
    }
    
    bootstrapApplication(App, {
      providers: [
        provideRouter(routes),
        { provide: RouteReuseStrategy, useClass: CustomReuseStrategy },
      ],
    });
    

    Stackblitz Demo