Search code examples
angularhttpbehaviorsubject

Angular BehaviorSubject isn't getting data from http call


I'm trying to work with behaviorSubject to get a username from db with http call. When a user logs in the process should be initialized. And then when a username would change I want to display that without reloading my page to get the updated value. I'm new to the concept and use of BehaviorSubject so this is what I have so far. I created a Authservice that takes the isAuthenticated$(boolean) observable from Auth0Service ( I use auth0 for authenication) and checks if a user is logged in. when a user is logged in it triggers my getLoggedInUser function that contains the http call to fetch the data. But when I want to display the name in sidenav it is null witch is the initial value of behaviorSubject. So I guesse I never make the call to fetch data.

AuthService:

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  constructor(private api: ApiService, private auth0Service: Auth0Service ) { 
    this.auth0Service.isAuthenticated$.pipe(tap(isAuth => {
      if (isAuth) {
       this.getLoggedInUser()
        console.log(isAuth)
      }
    }));


   }

  private userSubject$ = new BehaviorSubject<any>(null);
  user$ = this.userSubject$.asObservable();

  getLoggedInUser(): Observable<any> {
    return this.api.getCurrentLoggedInUser().pipe(tap(user=> this.userSubject$.next(user)));

  }


}

sidenav where I call on user$:

export class SidenavComponent implements OnInit {
 constructor(private authService: AuthService) {}

 getUser$ : Observable<User>;
 ngOnInit(): void {

    this.getUser$ =  this.authService.user$

 }

app component where I first initialize

export class AppComponent implements OnInit {
 constructor(public auth0Service: Auth0Service, private authService: AuthService) {}
 ngOnInit(): void {
    this.authService.user$;
 }
}

sidenav html:

   <div *ngIf="getUser$  | async as profile">
          <h3>{{profile.name}}</h3>
   </div>

Could anyone help me understand why I don't get any data?

UPDATE:

I've managed to verify that I first wasn't even getting in the getLoggedInUSer function but now I moved the this.getLoggedInUser() call to my authguard and now I could succesfully log the loggin boolean as true in my getLoggedInUser(). I now tried to log userSubject$ inside of getLoggedInUser() after the api call and I removed return before the call. I get this in console :

BehaviorSubject {_isScalar: false, observers: Array(0), closed: false, isStopped: false, hasError: false, …}
value: (...)
_isScalar: false
observers: [Subscriber]
closed: false
isStopped: false
hasError: false
thrownError: null
_value: null
__proto__: Subject

It seems empty witch is strange because I logged this after the api call


Solution

  • Your service should not call method in constructor this.auth0Service.isAuthenticated$.. These methods should be separated:

    import { HttpClient } from '@angular/common/http';
    import {Observable, of } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    
    @Injectable({
      providedIn: 'root'
    })
    export class AuthService {
        constructor(private api: ApiService, 
            private auth0Service: Auth0Service ) {}
    
    
        this.auth0Service.isAuthenticated$
            .pipe(map(v => v));
    
    
        getLoggedInUser(): Observable<any> {
            return this.api.getCurrentLoggedInUser()
                .pipe(map(v => v));
        }
    }
    

    And your component can get data from the above service using async and await keywords.

    your.component.ts:

    user: any;
    async ngOnInit() {
        let isAuthenticated = this.authService.isAuthenticated.toPromise();
        if (isAuthenticated) {
            this.user = this.authService.getLoggedInUser().toPromise();          
            console.log(this.user);
        }
    }
    

    HTML:

    <div *ngIf="user">
         <h3>{{user | json}}</h3>
    </div>
    

    UPDATE:

    You can use Observable for getting recently updated user:

    user: any;  
    
    ngOnInit() {
        this.authService.isAuthenticated.pipe(
            tap(val => console.log(val)),
            switchMap(val => this.authService.getLoggedInUser())
            ).subscribe(user => {
                this.user = user;
            });
    }