0

I have a simple function in my service:

  /**
   * Funtion to check if the user logged in has admin rights
   */
  isAdmin(user_id: string) {
    var adminTemp: boolean;
    this.httpClient
        .get(`${this.urk}/${user_id}`)
        .subscribe((data: any) => {
          if (data.includes('Admin')) {
            adminTemp = true;
          } else {
            adminTemp = false;
          }
        });
    return adminTemp;
  }

This function is called in a canActivate function which needs a bool value to determine if the user is an admin or not.

canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if(this.loginService.isAdmin('123')){
      return true;
    }
    else{
      return this.router.parseUrl("/admin/logout");
    }
  }

The problem is that before the api call is made, it takes a value of undefined and marks the user as non-admin. I understand this happens because subscribe itself is an async call. I could not find any other way to figure out how to do this, as the api response has to be processed before it can be known if they are a user or not, else I would have used a simple return statement. How should I proceed?

Rohit Kumar
  • 684
  • 2
  • 17
  • 39

1 Answers1

2

your problem is that you are not taking advantage of Reactive programming (which is one of the best advantage that Angular brings with help of Rxjs).

In your function isAdmin() it call http which is async, you are not making sure when you return adminTemp , it has been assigned with the value from http result.

Instead, you should not subscribe the http call from asAdmin, just return it as observable, and let canActive handle it. So try not to subscribe if you can avoid it, will help you improve the efficiency.

      isAdmin(user_id: string): Observable<boolean> {
        
        return this.httpClient
            .get(`${this.urk}/${user_id}`)
            .pipe(
               switchMap((data) => {
                 if (data.includes('Admin')) {
                   return of(true);
                 } else {
                  return of(false);
                 }
                })
             );
        
      }

The advantage is that canActive() can also return Observable, so you can easily use it in canActive() as following:

    canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        return this.loginService.isAdmin('123').pipe(
     map((res) => {
      if (res) {
         return true;
      } else {
         return  this.router.parseUrl("/admin/logout");   
      }
     })
 );

      }
Huantao
  • 847
  • 1
  • 11
  • 18
  • Thank you so much for the answer. In the canActivate() code, are you checking `if(res)` has a value? is this the logic used to make sure the http call is made and then only the response is checked? Also, the part where I check `if (data.includes('Admin')) {` is actually not direct, there is another property under which the permissions is. So it is like `if (data.groups.includes('Admin')) {`. But when I do that, i get an error in terminal ` Property 'groups' does not exist on type 'Object'.` Any ideas? – Rohit Kumar Mar 11 '21 at 22:36
  • 1
    you can try cast the response to the type when doing http call `http.get(...)` – Huantao Mar 12 '21 at 22:30
  • I still get an error from the angular terminal that `Type 'Observable' is not assignable to type 'boolean'`. Can I ignore that? – Rohit Kumar Mar 14 '21 at 19:45
  • 1
    I see, actually we need use switchMap here, your httpClient.get is not returning Observable, so we need use switchMap to convert it. I have updated the answer – Huantao Mar 14 '21 at 20:08
  • I did the changes you made, but it still gives the same error message. Also, can you tell me a way I can call the `isAdmin()` function in another component html page? Right now the `canActivate` function works fine, but I do not have a variable where I can use in html files to hide/show html components. – Rohit Kumar Mar 14 '21 at 20:17
  • 1
    use in html page, you can use it with async pipe `
    admin can see
    `
    – Huantao Mar 15 '21 at 19:23