Search code examples
angularasynchronousangular-guards

asynchronous code in an Angular guard doesn't open page


The setup is simple. I have a guard that guards a route. If the user's property locationSaved === true then we want to allow the user to enter the page.

If the first check of the browser token is false we want to make an HTTP request to see if maybe the most recent version of the user object's locationSaved property might be updated to true.

I did the server request and it returned true, so I expected it to open the page, but unfortunately it doesn't. I assume it has something to do with the asynchronous request I did in the page, because when I replace the server request code with a simple return true; then it does open the page.

Does anyone know how I can use this asynchronous request and navigate to the page if the server returns true?

This is my guard code. I set up so that it would make the asynchronous request and the server returned true. However, it doesn't navigate to the page.

import { Injectable } from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate} from '@angular/router';
import {AuthenticationService} from './auth.service';
import {Observable} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class LocationGuardService implements CanActivate {

  constructor(private authService: AuthenticationService) { }

  canActivate(route: ActivatedRouteSnapshot): boolean {
    console.log('entered the guard');
    if (this.authService.isLoggedIn()) {
      if (this.authService.currentUser().user.locationSaved) {
        return true;
      } else {
        this.authService.isLocationSaved()
          .subscribe((status: {saved: boolean}) => {
            console.log('saved', status);
            return status.saved;
          });
      }
    } else {
      return false;
    }
  }
}

Solution

  • It won't because in the else block, you're not returning anything. Instead of subscribeing, you should have mapped to status.saved

    Ofcourse, the canActivate method will now return a boolean or an Observable<boolean>. So you might want to change the return type of canActivate.

    This should work:

    import { Injectable } from '@angular/core';
    import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
    import { AuthenticationService } from './auth.service';
    import { Observable } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    @Injectable({
      providedIn: 'root'
    })
    export class LocationGuardService implements CanActivate {
    
      constructor(private authService: AuthenticationService) { }
    
      canActivate(route: ActivatedRouteSnapshot): boolean | Observable<boolean> {
        if (this.authService.isLoggedIn()) {
          if (this.authService.currentUser().user.locationSaved) {
            return true;
          } else {
            return this.authService.isLocationSaved()
              .pipe(map(status => status.saved));
          }
        } else {
          return false;
        }
      }
    }