Reputation: 9880
Edit: StackBlitz minimal repro seems to work, so there's something else amiss it seems..
Angular 6.1.3
I have a route that needs to be guarded based on the param slug
.
{
path: 'market/:slug',
component: MarketComponent,
canActivate: [RoleGuard],
},
RoleGuard canActivate
method:
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | Promise<boolean> | boolean {
const market$ = this.marketService.getBySlug(
route.paramMap.get('slug')
); // returns an observable of the corresponding market object
// Verify the user is authorized to see this market.
return this.authService.isAuthorized(market);
}
authService.isAuthorized:
isAuthorized(market: Observable<Market>): Observable<boolean> {
return combineLatest(this.user$, market).pipe(
map(([user, market]) => {
// Check roles of the user based on the market's configuration
return true;
})
);
}
I've removed the user/market logic just trying to strip out anything that could be causing problems, but when I return the above method inside canActivate
the routing stops. Route trace:
Router Event: NavigationStart
NavigationStart(id: 2, url: '/market/sams-corner-store')
NavigationStart {id: 2, url: "/market/sams-corner-store", navigationTrigger: "imperative", restoredState: null}
Router Event: RoutesRecognized
RoutesRecognized(id: 2, url: '/market/sams-corner-store', urlAfterRedirects: '/market/sams-corner-store', state: Route(url:'', path:'') { Route(url:'market/sams-corner-store', path:'market/:slug') } )
RoutesRecognized {id: 2, url: "/market/sams-corner-store", urlAfterRedirects: "/market/sams-corner-store", state: RouterStateSnapshot}
Router Event: GuardsCheckStart
GuardsCheckStart(id: 2, url: '/market/sams-corner-store', urlAfterRedirects: '/market/sams-corner-store', state: Route(url:'', path:'') { Route(url:'market/sams-corner-store', path:'market/:slug') } )
GuardsCheckStart {id: 2, url: "/market/sams-corner-store", urlAfterRedirects: "/market/sams-corner-store", state: RouterStateSnapshot}
Router Event: ChildActivationStart
ChildActivationStart(path: '')
ChildActivationStart {snapshot: ActivatedRouteSnapshot}
Router Event: ActivationStart
ActivationStart(path: 'market/:slug')
ActivationStart {snapshot: ActivatedRouteSnapshot}
During normal routing more Router events are triggered, but here it just stops. I'm thinking it has something to do with the isAuthorized
not emitting anything, but I'm stumped. After the failure, navigating anywhere doesn't do anything; the URL stays the same and nothing else is logged to the console.
At times the method isn't even called, console.log isn't called in the combineLatest
/withLatestFrom
.
I've tried combineLatest
and withLatestFrom
with some operator variants to no avail.
Upvotes: 4
Views: 2382
Reputation: 3649
canActivate guard will subscribe on returned Observable and waiting till that Observable is completed, until that moment no other router event will be handled.
Add first()
(Rx operator) to the user$ and market$ Observable.
PS instead of combineLatest
use forkJoin
.
Upvotes: 3
Reputation: 7672
You are doing it right, but a guard requires the observable to complete. So you are stuck waiting for it.
try doing
return this.authService.isAuthorized(market).pipe(
take(1)
);
this will take the first result and complete, which should fix your problem.
Upvotes: 2