3

I am writing a code that check if the browser has userInfo. If not, the browser calls an API to get the userInfo from server. Only if there is a valid userInfo, the user is allowed to navigate to the pages.

My problem is that canactivate() does not wait for the call to complete. I've tried Resolver which are mentioned in other threads, but it also didn't work.

So, I put async-await but it is also not working. canactivate() returns before getUser() is completed. Is the syntax of async-await is wrong in the below code?

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private router: Router, private refService: RefService, private userInfoService: UserInfoService) {
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {

    this.getUser();
    console.info('canActivate completed');
    return this.userInfoService.getUserInfo() !== null;
  }

  async getUser() {
    if(!this.userInfoService.getUserInfo()) {
      await this.refService.getTamUser().toPromise( ).then(data => {
        this.userInfoService.setUserInfo(data);
      });
    }
  }
}
barbarian
  • 31
  • 3
  • Does this answer your question? [Angular2 canActivate() calling async function](https://stackoverflow.com/questions/38425461/angular2-canactivate-calling-async-function) – jtbandes Nov 09 '20 at 03:05
  • Do you mind to check the answer that solved your problem, please? – unsignedmind Nov 11 '20 at 13:48

2 Answers2

2

can Activate is not (a)waiting for getUser. I would do it like this:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const userInfo = this.userInfoService.getUserInfo()
    if (userInfo) {
      return userInfo !== null;
    } else {
      return this.refService.getTamUser().pipe(switchMap((data: any) => {
        this.userInfoService.setUserInfo(data);
        return of(data !== null);
      })) 
    }
  }

First check if the user info is present. If not then get the user info from ref service and return a new observable of type boolean. A false boolean means either not allowed to access or refService call failed.

Read more about switchMap here. In short words: Make a new observable out of data from the original observable.

unsignedmind
  • 182
  • 1
  • 12
1

You can wrap your async logic inside the canActivate function itself as a promise, so that can canActivate will return only in the then or catch block of promise return. Do return as an observable.

 @Injectable()
export class AuthGuard implements CanActivate {

  constructor(private router: Router, private refService: RefService, private userInfoService: UserInfoService) {
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  if (!this.userInfoService.getUserInfo()) {
    this.refService.getTamUser().toPromise()
    .then(data => {
      if (data) {
        this.userInfoService.setUserInfo(data);
        console.info('canActivate completed');
        return of(true);
      } else {
        return false
      }
    })
    .catch(() => {
      return false;
    })
  } else {
    return of(this.userInfoService.getUserInfo() !== null);
  }
}
Ram
  • 451
  • 10
  • 18