How do I get a value in a BehaviorSubject to return within a getter?

Viewed 1483

From my understanding, when you subscribe to a BehaviorSubject, that subscription is asynchronous. I am trying to get a value from an object within a BehaviorSubject and return that value in a getter. The getter needs to return the type 'number'. How do I approach this?

Below, I have a getter userId, which should return the user Id from an object held in the BehaviorSubject _loggedInUser

export class AuthServiceService {

  private _loggedInUser: BehaviorSubject<LoggedInUserData> = new BehaviorSubject(null);

  constructor(
    private http: HttpClient, 
    private storage: Storage
  ) { }


 /// This method below!!! --------------

  public get userId(): number{

    this._loggedInUser.pipe(
      take(1), 
      map(((loggedInUserData: LoggedInUserData) => {
        if(loggedInUserData){
          return loggedInUserData.user_id
        }
        return null;
      }))
    ).subscribe((userId: number | null) => {
      return userId;
    });
    
  }
  
 //// -----------------


  public doLogin(loginData: LoginFormData): Observable<any>{

    return this.http.post<LoggedInUserData>(`${authURL}/token`, loginData)
    .pipe(
      catchError(err => {

        if(err.status && err.status === 403)
          return throwError('The credentials you have entered are invalid or do not exist.');
        else
          return throwError('There was an issue processing your request.');

      }), 
      tap(respData => {

        console.log('LOGGED IN USER DATA: ', respData);

        this._loggedInUser.next(respData);
        this.storage.set('userData', respData);

      })
    );



  }


  public async autoLogin(){



  }


  public doLogout(){




  }





}

3 Answers

Having an access to the Subject, you can obtain its value synchronously via getValue() mothod, as following:

public get userId(): number {
  return this._loggedInUser.getValue()?.userId
}

If you simply need the result synchronious, you should use the example as provided by Rafi Henig:

    public get userId(): number {
      return this._loggedInUser.getValue()?.userId
    }

If you want it to be async, you could return an Observable<number> instead and use an async pipe in your html if you need to show it there. If not, you can subscribe to that Observable where you need it or keep the subscription if you want to track changes of the value. I am not sure if this is what you want, but here is an example of that:

  public get userId(): Observable<number> {
    return this._loggedInUser.pipe(
      map((item) => item?.userId)
    );
  }

For HTML, use: {{ userId | async }} and for typescript code you can use:

    this.userId.pipe(take(1)).subscribe((userId) => {
      console.log(userId);
    });

If you want to keep track the changes in your typescript, you should remove take(1) and simply subscribe to it. Remember to clean the subscription in ngOnDestroy.

Another approach - convert Observable to Promise, then use async/await:

const user = await this._loggedInUser.toPromise();
return user && user.userId;
Related