I have an angular 4.3.6 application with lazy-loaded modules. Here is a partial root router:
const routes: Routes = [
{ path: '', redirectTo: 'fleet', pathMatch: 'full' },
{
path: '',
component: AppComponent,
canActivate: [AuthenticationGuard],
children: [
{
path: 'fleet',
loadChildren: "./modules/fleet.module",
canActivate: [AuthenticationGuard]
},
{
path: 'password/set',
loadChildren: "./modules/chooseNewPassword.module",
canActivate: [ChoosePasswordGuard]
}
]
}
]
// Exports RouterModule.forRoot(routes, { enableTracing: true });
My child routers within these two example modules:
Fleet:
RouterModule.forChild([
{
path: '',
component: FleetComponent,
canActivate: [AuthenticationGuard]
}
]);
Choose New Password:
RouterModule.forChild([
{
path: '',
component: ChooseNewPasswordComponent,
canActivate: [ChoosePasswordGuard]
}
]);
The AuthenticationGuard
calls a method that looks like this:
return this.getUserSession().map((userSession: UserSession) => {
if (userSession && userSession.ok) {
return true;
}
else if (userSession && userSession.expired) {
this.router.navigate(['password/set'])
.catch((e: Error) => console.error(e));
return true;
}
else {
window.location.replace('/');
return false;
}
}
So, if the user's session is ok, it activates the route. If the user's password is expired, it redirects the user to the choose new password module. If no session, redirects to login.
The ChoosePasswordGuard
does a similar thing, but only protects the choose new password component (a different facility is used for setting passwords generically):
return this.getUserSession().map((userSession: UserSession) => {
if (userSession) {
return userSession.expired;
}
else {
return false;
}
});
This worked before module splitting.
Now, I'm stuck in a redirection loop. With router tracing on, I observe the following sequence. The user logs in and the AuthenticationGuard
corrects redirects to the /password/set module, and is handed off to ChooseNewPasswordGuard
:
- NavigationStart(id: 4, url: '/password/set')
- RoutesRecognized {id: 4, url: "/password/set", urlAfterRedirects: "/password/set", state: RouterStateSnapshot}
- GuardsCheckStart {id: 4, url: "/password/set", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
- GuardsCheckEnd {id: 4, url: "/password/set", urlAfterRedirects: UrlTree, state: RouterStateSnapshot, shouldActivate: true}
- NavigationCancel {id: 4, url: "/password/set", reason: ""}
And the this loop repeats.
(It also repeats if I replace the whole ChooseNewPasswordGuard with return Observable.of(true);
)
EDIT: I am redirected to the root page (/
) even when I provide /#/password/set
in the URL bar...
Questions:
What have I done wrong in my router(s) or guards to force this loop now that modules are lazy-loaded? I'm particularly confused by
shouldActivate: true
followed byNavigationCancel reason: ""
.Does it have something to do with the fact that I'm redirecting directly in the AuthenticationGuard, and now that this guard is applied to my main empty root route (
{ path: '', redirectTo: 'fleet', pathMatch: 'full' }
) it's always called and redirects, even once I've set the path?Do I actually need to repeat the
canActivate
guard in my child route and my root route?As usual, any other comments are welcome.