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
:
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 by NavigationCancel 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.
The problem was that I was over-applying the AuthenticationGuard
: it should not have been applied to the top-level AppComponent because it will always redirect to the Choose New Password module, even when it is loading that module.
My root routes
should have looked like this:
const routes: Routes = [
{ path: '', redirectTo: 'fleet', pathMatch: 'full' },
{
path: '',
component: AppComponent,
// canActivate: [AuthenticationGuard], // <-- Remove this guard
children: [
{
path: 'fleet',
loadChildren: "./modules/fleet.module",
canActivate: [AuthenticationGuard]
},
{
path: 'password/set',
loadChildren: "./modules/chooseNewPassword.module",
canActivate: [ChoosePasswordGuard]
}
]
}
]
(I welcome and will happily Accept better explanations or better AuthenticationGuard patterns.)