0

I have an Angular application with in it a UserService and a guard. That guard is using the UserService to create a user and using that user based on its role to decide to return true or false. See the following code:

This is the guard:

  canActivate(): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

  return new Observable<boolean>(obs => {
    this.userService.getUser().subscribe(user => {
      if (user.roles.includes(Roles.IT_PROFESSIONAL)) {
        obs.next(true);
      } else {
        this.router.navigate(['/']);
        obs.next(false);
      }
    });
  });
}

This is the UserService Class:

  private static user: User;

  public getUser(): Observable<User> {

    if (!UserService.user) {

        UserService.user = new User();
        this.getGroups()
        .subscribe((response: any) => {
          response.value.map((v: any) => {  
            this.setRol(v); // use v to push v to the role array of the user
          });

          return of(UserService.user);
        });
    }
    else{
      return of(UserService.user);
    }
  }

    private getGroups(): Observable<any> { // Get groups for user
      return this.http.get("https://graph.microsoft.com/v1.0/me/memberOf");
    }

When I run the app it says "Cannot read property 'subscribe' of undefined". The problem is that the guard subscribes on the getUser observable, but this that I gets the user observable immediately. Why doesn't it wait for the UserService to return the user with the statement "return of(UserService.user)"?

1
  • thanks for your answer! Now it says this "Cannot set property 'roles' of undefined" in the setRole method which basically does this: " if(v.mailNickname == "Employees") { UserService.user.roles = [Roles.IT_PROFESSIONAL]; }" Commented Feb 18, 2021 at 14:08

1 Answer 1

1

I don't see the need to subscribe to the observable in both instances. You could modify the notification using map or perform side-effects using tap operator and simply forward the observable. Try the following

Guard

canActivate(): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
  return this.userService.getUser().map(user => {
    if (user.roles.includes(Roles.IT_PROFESSIONAL)) {
      return true;
    }
    this.router.navigate(['/']);
    return false;
  });
}

UserClass service

public getUser(): Observable<User> {
  return (!!UserService.user)
    ? of(UserService.user)
    : this.getGroups().pipe(
        map((response: any) => // <-- not sure if `response` is modified here, if not use `tap`
          response.value.map((v: any) => this.setRol(v))
        )
      );
}

Update: Initializing UserService.user

private static user: User;    // <-- don't initialize here
public getUser(): Observable<User> {
  return (!!UserService.user)
    ? of(UserService.user)
    : this.getGroups().pipe(
        tap((response: any) =>
          response.value.forEach((v: any) => this.setRol(v))
        )
      );
}

setRol(v: any) {
  if (!UserService.user) UserService.user = { /* valid user object */ } // <-- initialize here
  if (v.mailNickname == "Employees")  { 
    UserService.user.roles = [Roles.IT_PROFESSIONAL]; 
  }
}
Sign up to request clarification or add additional context in comments.

12 Comments

thanks for your answer! Now it says this "Cannot set property 'roles' of undefined" in the setRole method which basically does this: " if(v.mailNickname == "Employees") { UserService.user.roles = [Roles.IT_PROFESSIONAL]; }"
You've only defined the variable user. You need to initialize it to use it: Eg. private static user: User = Object.create(null).
BTW if you're using this static property elsewhere in the app, it's better to define it as a multicast observable instead. Eg. user: ReplaySubject<User> = new ReplaySubject<User>(1). This way you get to keep the data reactive.
Cannot read property 'includes' of undefined. this is hapenning in the guard when the include comparison is done.. I have a feeling something is really wrong with my code
I don't think it even does an call with by using getGroups. Because the error is showing up too fast.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.