Home > front end >  Angular - display updated behaviorsubject value in template when value changes
Angular - display updated behaviorsubject value in template when value changes

Time:11-24

I have a boolean BehaviorSubject whose value I want to use in my template:

isLoggedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

in ngOnInit, I subscribe to it:

this.isLoggedIn$.subscribe((result) => {
  console.log('result', result);
});

Following that (in the ngOnInit), I have a service call that returns a promise. When that is done, I set the behavior subject value:

  this.authService.isLoggedIn().then((loggedIn) => {
    if (loggedIn) {
      this.isLoggedIn$.next(loggedIn);
      console.log('loggedIn', loggedIn);
      // set the user model value here...
    }
  });

Lastly, I am displaying this value in the template:

<span>{{ isLoggedIn$ | async }}</span>

However, even though the console is showing the value changing, the template is not updating. I tried ngAfterViewInit and change detection and nothing seems to work. I assume the reason the value isn't updating in the template is becasue angular doesn't think anything has changed when the behavior subject is updated.

Am I missing something?

The problem is it might take a second or two to get the value back from the authService.isLoggedIn promise and if it is delayed longer that, the template wont display data (login info, like the user name) from the promise. The only thing that works is if I use a setTimeout() wrapper around the service call, which I would prefer not to do.

Any suggestions?

EDIT: here is the whole component, adding the Observable as suggested by Brandon. Still doesn't work though:

import { Component, OnInit } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { IUserModel } from './_interfaces/user';
import { AppService } from './_services/app.service';
import { AuthService } from './_services/auth.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})

export class AppComponent implements OnInit {
  apiUser?: IUserModel;
  isLoggedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
  isLoggedInOb: Observable<boolean> = new Observable<boolean>;

  constructor(
    private readonly authService: AuthService,
    private readonly appService: AppService) {
  }

  ngOnInit(): void {
    this.isLoggedInOb.subscribe((result) => {
      this.isLoggedIn = result;
      console.log('result', result);
    });

    setTimeout(() => {
      this.authService.isLoggedIn().then((loggedIn) => {
        this.isLoggedIn$.next(loggedIn);
        this.isLoggedInOb = this.isLoggedIn$.asObservable();
        console.log('loggedIn', loggedIn);

        this.appService.lastActivityDatetimeSubject$.subscribe((data) => {
          this.lastActivityDatetime = data;
        });
      });
    }, 0);

    this.appService.apiUserSubject$.subscribe((data) => {
      this.apiUser = data;
    });

  }

  login(): void {
    this.authService.login();
  }

  logout(): void {
    this.authService.logout();
  }
}

If I set the setTimeout to 1000 milliseconds, it works but not if I remove the setTimeout function.

Here is the template:

<header>
  <div >
    <a href="index.html" >
      <img src="assets/images/logo.png"  alt="Company Logo" />
    </a>
    <div >
      <span >Company Name</span>
    </div>
  </div>
  <div>
    <div >
      <div >
        <span>App Name</span>
      </div>
      <div >
        <span *ngIf="apiUser && isLoggedIn && apiUser.FirstName">Hello {{apiUser.FirstName}} {{apiUser.LastName}}!</span>
        <button  *ngIf="!isLoggedIn" mat-stroked-button (click)="login()">Login</button>
        <button  *ngIf="isLoggedIn" mat-stroked-button (click)="logout()">Logout</button>
        
        {{ isLoggedInOb | async }} <--- this is what I am trying to have updated when the behavior subject is updated
        
      </div>
    </div>
  </div>
</header>

<div  role="main">
  <router-outlet></router-outlet>
</div>
<footer  role="contentinfo">
  <div >
    <a href="/disclaimer">Disclaimer</a>
    <span  aria-hidden="true">|</span>
    <a href="/privacy-policy">Privacy Policy</a>
    <span  aria-hidden="true">|</span>
    <a href="/terms-conditions">Terms and Conditions</a>
  </div>
</footer>

CodePudding user response:

I typically provide an observable for the Subject/BehaviorSubject:

isLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
isLoggedIn$: Observable<boolean>;

constructor() {
  this.loggedIn$ = this.isLoggedIn.asObservable();
}

Then components subscribe to the observable, not the Subject/BehaviorSubject directly.

CodePudding user response:

try to use this logic code :

IN THE SERVICE :

private isLoggedIn: BehaviorSubject<boolean> = new 
BehaviorSubject<boolean>(false);
isLoggedIn$ = this.isLoggedIn.asObservable();

isLoggedIn(){
return this.http.post(url).pipe(
map(data)=>{
//...code
this.isLoggedIn.next(data)
//...code
}
)
}

IN THE COMPONENT

currentUser$:Observable<UserInteface>

ngOnInit(): void {

this.currentUser$= this.service. isLoggedIn$

}

IN THE HTML you can subscribe to it and unsub using the async pipe

<div *ngIf="(currentUser$ | async) as user">

{{user.nameExemple}}

//some code 

</div>
  • Related