Phix
Phix

Reputation: 9880

Route guard method breaking routing

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

Answers (2)

Buggy
Buggy

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

Leon Radley
Leon Radley

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

Related Questions