0
votes

My apologies if this is posted somewhere on Stack Overflow, but I'm not finding a answer that works for me.

I have a BehaviorSubject that tracks a state as a boolean value. I also have a public getter method that is an Observable that returns the BehaviorSubject. I do this so that not only can I subscribe to changes in the BehaviorSubject value, but to also get the current value when it hasn't changed. While I'm able to get the proper value from subscribe, I am getting an Object returned when just calling the getter. I want it to return the last value.

I've tried using .getValue(), which does not seem to work, and I've tried .value, which does seem to work but throws an error in the IDE.

I've created a very basic example of what I'm trying to accomplish and I've put comments in the my.service.ts file to show what I'm going for. Please advise. Thank you!

EDIT: I used what was in stack blitz by default and assigned the getter to the name variable just to see it. My goal is to get the value so that I can do something based on wether the value is true or false. I'm sure I'm missing something really simple.

EDIT: Adding code to question as requested

EDIT: I've also added the .subscribe to show that while I understand that's how Observables work, I also want to check the value OUTSIDE of a value change using the same getter if it is possible.

MyService

import { Observable, BehaviorSubject } from "rxjs";

export class MyService {

  constructor() {}

  private _isBoolean = new BehaviorSubject<boolean>(false);

  public get isBoolean(): Observable<boolean> {
    //return this._isBoolean.value; // Returns boolean, but throws error "Type 'boolean' is not assignable to type 'Observable<boolean>'."
    return this._isBoolean; // Returns object, but I want the value
  }

}

AppComponent

import { Component } from '@angular/core';
import { MyService } from './my.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name;

  constructor(private _myservice: MyService) {
    this._myservice.isBoolean.subscribe(val => {
      console.log("value from subscribe: " + val)
    })

    this.name = this._myservice.isBoolean;
    if (this._myservice.isBoolean) {
      console.log("true!");
    } else if (!this._myservice.isBoolean) {
      console.log("false!");
    }
  }
}

https://stackblitz.com/edit/angular-hhdgrc

4
You should put your code in your answer - bryan60
For this, it is needed to define new value like (subjectBoolean). And whenever emit the value to BehaviorSubject, set that subjectBoolean value together and on getter method, returns that subjectBoolean. - Derek Wang
@Derek.W Can you give a coded example? I'm not following what you're saying. Thank you. - AncientSpice
or you can get the value like this: this._isBoolean.getValue() on MyService, then it will work. - Derek Wang
You can use BehaviorSubject directly, no need to use observable. So it's good t make BehaviorSubject variable as public and on getIsBoolean return the rela boolean value only using BehaviorSubject.getValue() - Derek Wang

4 Answers

3
votes

You are using Observables in wrong way. Please add a subscribe call to the observable:

this._myservice.isBoolean.subscribe((val) => {
    this.name = val;
    console.log("value: " + val);
});

You can just add code in subscribe callback to use the changing value:

Edited app.component.ts

import { Component } from '@angular/core';
import { MyService } from './my.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name;

  constructor(private _myservice: MyService) {
    this._myservice.isBoolean.subscribe(val => {
      console.log("value from subscribe: " + val);


      this.name = val ;
      if (val) {
        console.log("true!");
      } else if (!val) {
        console.log("false!");
      }
    })

  }
}
1
votes

Correct way to use Boolean BehaviorSubject :-

define like this:-

_isBoolean: BehaviorSubject<boolean> = new BehaviorSubject(false);

update like this :-

this._isBoolean.next(true); //  for true
this._isBoolean.next(false); // for false

read value in some other component : -

this._isBoolean.subscribe(value => {
console.log(value);
    });

read current value immediately only one time :-

this._isBoolean.asObservable().pipe(take(1)).subscribe(value => {
    console.log(value);
  });
0
votes

The problem is in the html part of the app the service returns an observable for which the template has to subscribe. For this purpose you have to add async pipe to the template variable

Add the async pipe to the app component.html variable. This will do the trick

<hello name="{{ name|async }}"></hello>
0
votes

Thanks goes out to Rahmat Ali for leading me to the answer I needed. I'm providing an answer here to make it easier to find, provide a simpler example of the solution, and a better explanation.

I wanted to have a getter function that allowed me to both subscribe to changes to the value of a BehaviorSubject and also be able to get the current value without a subscription depending on the current need.

The reason it wasn't working is that in following another example, I made the getter return an Observable type rather than a BehaviorSubject type. So, I was getting a "truthy" result in the console because I was checking for the existence of it rather than the boolean value. Once that changed, I was able to access the boolean value of the BehaviorSubject with the .value property.

Here is a simplified example of the solution (NOTE: the getter was unnecessary because once the getter returned the BehaviorSubject, I could change its value using .next without creating a setter):

import { Component } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';

  private _isBooleanSubejct = new BehaviorSubject<boolean>(false);

  public get isBoolean(): BehaviorSubject<boolean> {
    return this._isBooleanSubejct;
  }

  constructor() {
    console.log(this.isBoolean.value);
    this.isBoolean.subscribe(val => {
      console.log(val); // false until the timer completed
      console.log(this.isBoolean.value); // false until the timer completed
    });

    setTimeout(() => {
      this.isBoolean.next(true);
    }, 5000);
  }
}