简体   繁体   中英

Angular - How to avoid multiple http calls in canActivate

I have an Angular App with two authGuard Services:

export class AuthGuardService implements CanActivate {
  constructor(public auth: AccountService, public router: Router) {}
  canActivate(): Observable<boolean> {
    return this.auth.identity().pipe(
      map(account => {
        if (account) {
          return true;
        }
        this.router.navigate(['welcome']);
        return false;
      })
    );
  }
}

@Injectable()
export class StartupService implements CanActivate {
  isAdmin: boolean;
  constructor(public auth: AccountService, public router: Router, private sidebarService: SidebarService) {}
  canActivate(): Observable<boolean> {
    return this.auth.identity().pipe(
      switchMap(account => {
        this.isAdmin = account.isAdmin;
        return this.sidebarService.getCompanies(account.id.toString());
      }),
      map(firms => {
        if(this.isAdmin && Object.keys(firms).length === 0) {
          this.router.navigate(['startup']);
          return false;
        }
          return true;
      }),
      catchError(() => of(false))
    );
  }
}

In the AuthGuardService I call the identity service for checking if I'm logged or not, if not I need to redirect to the welcome page; in the StartupService I invoke the same service and I also check if I'm admin and I have some data available (companies). I also invoke the same service in the ngOnInit in the AppComponent and with ngrx I save the account state in the store. I invoke the same service for three times. Which is the best way to avoid this situation? Where can I use ngrx selector? I see in the debug that canActivate is invoked before the ngOnInit AppComponent. Any suggestions?

export class AuthGuardService implements CanActivate {
  constructor(
    public auth: AccountService
    public router: Router
  ) {}

  canActivate(): Observable<boolean> {
    return this.auth.identity().pipe(
      map(account => {
        if (account) {
          return true;
        }
        this.router.navigate(['welcome']);
        return false;
      })
    );
  }
}

@Injectable()
export class StartupService implements CanActivate {
  constructor(
    private auth: AccountService,
    private router: Router,
    private sidebarService: SidebarService
  ) {}

  canActivate(): Observable<boolean> {
    return this.auth.identity().pipe(
      switchMap(account => this.sidebarService.getCompanies(account.id.toString())
        .pipe(
          map(firms => {
            if(account.isAdmin && Object.keys(firms).length === 0) {
              this.router.navigate(['startup']);
              return false;
            }
            return true;
          }),
          catchError(() => of(false)) // you probably also want some redirection on error
        )
      ),
    );
  }
}

In your setup more important is how you have implemented AccountService.identity If it returns some observable that has current state of user identity something like this:

class AccountService {
  identity$ = new ReplaySubject(1);

  identity() {
    return this.identity$;
  }

  authenticate() {
    this.http.post('/authentication').subscribe((identity) => {
      this.identity$.next(identity)
    })
  }

  logout() {
    this.identity$.next(null)
  }
}

It will call HTTP only if you call authentication and your user identity will be stored in identity$

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM