0

I'm trying to create a RouterGuard in Angular that gets a response from the backend about the User's login status. The problem is that the HTTP request returns a Subscription with async concrete values. How can I guarantee the Router will "wait" for the http call to be transformed into a real value and then be processed?

AlreadyLoggedRouterGuard:

export class AlreadyAuthIn implements CanActivate {
  constructor(private clientService: ClientService,
              private router: Router) {  }
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

return this.clientService.isUserLogged()
  .subscribe(
    (response: Response) => {
      let loginStatus = response.message;
      if (loginStatus == 'not_logged') {
        localStorage.clear();
        return false;
      } else {
        return true;
      }
    });
}

Note: clientService.isUserLogged() is the http call, and will return a Observable.

This code already shows an Error saying that should not return a Subscription. Then I tried to assign the logic to a variable like:

let logged = this.clientService.isUserLogged().subscribe(...<logic>......)
if (logged) {
  return false;
} else {
  return true;
}

But that creates a new problem: race condition. The If statement is checked before the Subscription turns into a Boolean value, and the program processes the logic wrongly. Maybe we can solve this with a Promise?

1 Answers1

1

You are now returning a subscription not the result of your request. Use a map function to turn the return type of your request into an Observable<boolean> which will be used by angular to handle the route guard.

Try something like (untested)

export class AlreadyAuthIn implements CanActivate {
  constructor(private clientService: ClientService,
              private router: Router) {  }
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

return this.clientService.isUserLogged()
  .pipe(map((response: Response) => {
      let loginStatus = response.message;
      if (loginStatus == 'not_logged') {
        localStorage.clear();
        return false;
      } else {
        return true;
      }
    });
}
Fussel
  • 1,740
  • 1
  • 8
  • 27
  • That magically worked, thanks.. So if we return an Observable (instead of the Subscription) to the CanActivate function it will handle the async attribution? What about the navigators? Should we use them inside the pipe? – João Renault Nov 18 '22 at 12:32
  • Would be great if you could also accept the awnser. Not sure what you mean with "the navigators", could you elaborate on that? – Fussel Nov 19 '22 at 10:44
  • What I mean is something like ** this.router.navigate([''], {skipLocationChange: true}); return false; ** Note: I've already checked it works this way, thanks =) – João Renault Nov 29 '22 at 14:43