Search code examples
angularrxjsangular-routerbehaviorsubjectangular-guards

Angular15 - canActivate based on values stored in BehaviorSubject in oher service


I have PermissionsService which stores user's permissions. These are loaded from API by HttpClient after user's successful auth process (after login I call the getUserGroupsWithModelPermissions() which loads user's permission groups, and then I sum these permissions into _resolvedUserPermissions BehaviorSubject)

In every component I need these user's permissions, I subscribe to resolvedUserPermissions$, and after receiving data, I use other methods (ex. resolveModelPermission() where I pass data from subscribe) from ModelPermissionsService to do business logic and it works well.

Example usage in components:

  ngOnInit() {
    this.$permissions = this.permissionsService.resolvedUserPermissions$.subscribe(data => {
      this.canSeeProducts = this.permissionsService.resolveModelPermission(data, 'Products', PermissionScope.Read);
    })
  }

Permissions Service:

export class ModelPermissionService {

  private _resolvedUserPermissions = new BehaviorSubject<ModelFieldPermissions[]>([]); 
  public resolvedUserPermissions$ = this._resolvedUserPermissions.asObservable();

  public getUserGroupsWithModelPermissions(): void {
    this.httpClient.get<ApiResponse<UserGroups[]>>(userEndpoint + '/me/permissions', httpOptions)
    .subscribe({
      next: data => {
        this._resolvedUserPermissions.next(this.sumUserPermissions(data.data));
      },
      error: err => {
        console.error(err);
      }
    })
  }

public resolveModelPermission(userPermissions: ModelFieldPermissions[], modelName: string, permissionScope: PermissionScope):boolean {
    if(userPermissions.length > 0){
      // DO LOGIC HERE
    }
    return false;
  }


}

Now I need to use data from resolvedUserPermissions$ in canActivate method in router guard service but I don't know how to subscribe to this resolvedUserPermissions$?

I need something like this:

canActivate(){
    this.modelPermissionService.resolvedUserPermissions$.subscribe(data =>{
        return this.modelPermissionService.resolveModelPermission(data, 'modelName', scope)
    })
}

All methods I found used pipe+map instead of subscribe but I had no permissions data. Example:

canActivate(){
    return this.getAccess()
}

getAccess(){
    this.permissionsService.resolvedUserPermissions$.pipe(
        map(data =>{
            //here data is empty, no logic to do, because no subscription?
        })
    )
}

Solution

  • Permission Service:

    export class ModelPermissionService {
    
     getUserGroupsWithModelPermissions(): Observable<ModelFieldPermissions[]> {
        this.httpClient.get<ApiResponse<UserGroups[]>>('yourpath', httpOptions)
          .pipe(
            map(data => this.sumUserPermissions(data.data)),
            shareReplay(1), // probably you want to hold and re-use result from api
            tap(data=> concole.log(data)), // DEBUGGING
          );
      }
    
      resolveModelPermission(data, 'modelName', scope): Observable<boolean> {
        return this.getUserGroupsWithModelPermissions().pipe(
          map(data=> / your code to extract permission from user data /),
        )
      }
    }
    

    in Resolver Service:

    canActivate() {
        return this.modelPermissionService.resolveModelPermission(data, 'modelName', scope)
          .pipe(
            take(1), // Observable must complete!
            tap(data=> concole.log(data)), // DEBUGGING
          ),
    }