Search code examples
angularhttp-redirectfragmentangular-router

Angular Router - redirect to route with fragment without adding to history


I have this route in my routing module:

{ path: 'signup', component: SignupComponent },

But this route shouldn't be accessed without fragments (#first-step, #second-step), so I've added some redirect logic into my app:

this.route.fragment.subscribe((fragment: string | null) => {
  if (fragment && ['first-step', 'second-step'].includes(fragment)) {
    // Fragments are correct, further logic with displaying step components
  } else {
    this.router.navigate(['signup'], {fragment: 'first-step', replaceUrl: true});
  }
})

I use navigate because fragments are client part of the url and it can be changed only on client, but there is a problem with browser history, with this approach it pushes poth pages - signup and signup#first-step. skipLocationChange will not help here as it doesn't change url. I can simply define fragment in registration button on main page but what if user will open signup page directly from the bookmarks or address bar? I would like to have nice redirect without adding signup page to the history, how can it be done? Is there anything I don't understand correctly?


Solution

  • You could move the logic to a Guard, so it gets process before the navigation completes.

    @Injectable({ providedIn: 'root' })
    export class SignupGuard implements CanActivate {
      constructor(private router: Router) {}
    
      canActivate(route: ActivatedRouteSnapshot): boolean | UrlTree {
        const {fragment} = route;
        
        // has required fragment proceed with navigation
        if (fragment && ['first-step', 'second-step'].includes(fragment)){
          return true;
        }
        
        //no fragment present, return UrlTree to 'signup#first-step' for redirection
        return this.router.createUrlTree(['signup'], { fragment: 'first-step' });
      }
    }
    

    And then use the guard in the Route:

    {
      path: 'signup',
      component: SignupComponent,
      canActivate: [SignupGuard],
      runGuardsAndResolvers: 'always',
    },
    

    I added the runGuardsAndResolvers so the guard also gets processed while trying to navigate to signup when on signup#first-step or signup#second-step. You can remove it if that's not possible within your app.

    Cheers