I'm having issues with Angular routing that don't seem to make much sense. With the following set-up, this outcome occurs...
/
/login
works as expected.However...
/activate
(Route in the account module)/login
I'm struggling to understand why auth guard is running for the /activate
route when it's not a child of the dashboard layout.
App routes
{
path: '',
component: DashboardLayoutComponent,
canActivate: [AuthGuard],
canActivateChild: [DashboardGuard],
children: [
{
path: 'schedule',
loadChildren: () =>
import('@libs/schedule').then(
i => i.ScheduleModule
),
data: {
breadcrumb: 'Schedule'
}
},
// Other feature modules omitted
{
path: '',
redirectTo: '/schedule',
pathMatch: 'full'
}
]
}
Account Routes
{ path: 'login', component: LoginComponent },
{ path: 'activate', component: ActivateComponent }
Auth Guard
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private router: Router,
private accountService: accountService
) { }
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
console.log('Auth Guard Start'); // <-- This appears in console
return this.jwtService.getToken().pipe(
map(token => {
if (!token) {
this.router.navigate(['/login']);
return false;
}
// Attempt to populate the user using the token.
this.accountService.populate();
return true;
}),
take(1)
);
}
}
App Module
@NgModule({
declarations: [AppComponent, DashboardLayoutComponent],
imports: [
// .. other modules omitted
AccountModule,
AppRoutingModule
],
providers: [AuthGuard, DashboardGuard],
bootstrap: [AppComponent]
})
export class AppModule { }
Additional information
This happens when running in production mode only.
Augury reports that /login
and /activate
are both siblings of the /
route. https://i.sstatic.net/2LDY4.jpg
@angular/core: 8.2.6
@angular/router: 8.2.6
This issue was actually caused by my ngrx-router
implementation where I was defining its initial state.
Originally initial state was set in the following way as suggested here - https://github.com/ngrx/platform/issues/835#issuecomment-369592809
export const routerInitialState: fromRouter.RouterReducerState<RouterStateUrl> = {
state: {
url: window.location.pathname,
queryParams: getQueryParams(window.location.search.substring(1)),
params: {}
},
navigationId: 0
}
This will not work in production builds. Navigating directly to a route like /activate
, for example, causes a discrepancy between router state and angular router state, cancelling the navigation.
The correct way top set the initial router state is as follows...
export const routerInitialState: fromRouter.RouterReducerState<RouterStateUrl> = {
state: {
url: '/',
queryParams: {},
params: {}
},
navigationId: 0
}
With initialNavigation: 'enabled'
in app-routing.module
, the router state is updated at the earliest possible point and is synced with the angular router.