I have three routes, one of them protected with a guard
app.routing-module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './modules/auth/login/login.component';
import { DashboardComponent } from './modules/dashboard/dashboard.component';
import { CustomersComponent } from './modules/customers/customers.component';
import { AuthGuard } from './shared/guards/auth.guard';
const routes: Routes = [
{
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{ path: 'home', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
{ path: 'customers', component: CustomersComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [AuthGuard]
})
export class AppRoutingModule { }
The guard gets if user is auth from ngrx store and returns an Observable to allow or not to activate the route
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router
} from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { catchError, map, switchMap, filter, take } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
import { Store } from '@ngrx/store';
import { getUserAuth } from '../../store/reducers';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private store: Store<any>, private router: Router) { }
getUserAuth(): Observable<any> {
return this.store.select(getUserAuth).pipe(
filter((data: any) => data),
take(1)
);
}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return this.getUserAuth().pipe(
switchMap(() => of(true)),
catchError(() => of(false))
);
}
}
When switching between the two "public" routes the navigation works fine, but once trying to access to the protected route and gets cancelled (user is not auth), the navigation is cancelled and none of the routes can be accesed. On the other hand if the user is logged, the navigation works fine and the three routes are accesible.
If I return a simple Observable.of(false) in the canActivate method, the protected route can not be accesed but the navigation to the other routes still works
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return Observable.of(false);
}
Why is the navigation cancelled?
EDIT
I forgot to say that I'm using @ngrx/router-store so my app.module looks like:
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule,
StoreModule.forRoot(rootReducers, {metaReducers}),
StoreRouterConnectingModule.forRoot({
stateKey: 'router' // name of reducer key
}),
StoreDevtoolsModule.instrument({ maxAge: 25 }),
EffectsModule.forRoot(rootEffects),
AuthModule,
DashboardModule,
CustomersModule
],
providers: [{ provide: RouterStateSerializer, useClass: CustomSerializer }],
bootstrap: [AppComponent]
})
The problem lies here:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private store: Store<any>, private router: Router) { }
getUserAuth(): Observable<any> {
return this.store.select(getUserAuth).pipe(
filter((data: any) => data),
^^^^^^
take(1)
);
}
The AuthGuard will pause navigation until the Observable
resolves to either true
or false
. Since you're filtering any falsy null
values, the Observable
will not emit anything when the user is not authenticated. You should map the values to true
/ false
instead:
return this.store.select(getUserAuth).pipe(
map((data: any) => !!data),
^^^^^^^^^^^^^^^^^^^^^^^^^
take(1)
);