'Take once only when a boolean changes from false to true

I have multiple boolean properties which are false by default, but that may only be set to true at the start of the application. Should the property become true, I should take action once and then unsubscribe.

Here is a simple representation in a class. The default value of source is false, and an on going interval will set it to true. I used an interval over a timeout here to ensure it only occurs once in my test.

export class MyCoolClass {

  private source = new BehaviorSubject(false);
  source$ = this.source.asObservable();
 
  constructor() {
    this.source$.subscribe((value) => {
      console.log('my source', value);
    });

    window.setInterval(() => {
      this.source.next(true);
    }, 2000);
  }

}

What is a clean way to handle this? A long time ago I wrote some annoying subscription:

export class MyCoolClass {

  private source = new BehaviorSubject(false);
  source$ = this.source.asObservable();

  private mySubscription?: Subscription;
 
  constructor() {
    this.mySubscription = this.source$.subscribe((value) => {
       if(value === true){
          this.mySubscription?.unsubscribe();
          console.log('my source', value);
       }
    });

    window.setInterval(() => {
      this.source.next(true);
    }, 2000);
  }

}

I feel as though I always end up with a subscription that I need to deal with.

Is there a cleaner way to achieve this?

Thanks 😊

EDIT

I believe this works, but it still seems a little bloated?

export class MyCoolClass {

  private source = new BehaviorSubject(false);
  source$ = this.source.asObservable();

  constructor() {
    this.source$
    .pipe(
        skipWhile(value => value === false),
        first()
    )
    .subscribe((value) => {
      console.log('my source', value);
    });

    window.setInterval(() => {
      this.source.next(true);
    }, 2000);
  }

}


Solution 1:[1]

One way to simplify this would be :

export class MyCoolClass {

  private source = new Subject();
  source$ = this.source.asObservable();

  constructor() {
    this.source$
    .pipe(
        first()
    )
    .subscribe((value) => {
      console.log('my source', value);
    });

    window.setInterval(() => {
      this.source.next(true);
    }, 2000);
  }

}

Notably, making the BehaviourSubject into a Subject. BehaviourSubject specifically is for providing a default value to any subscribers. But in your case, you don't want a default value, infact you're skipping it entirely. So at this point, it would be simpler to simply move to a Subject and remove the skipping.

For a little more info on Subject vs BehaviorSubject (And ReplaySubject) here's a quick article : https://tutorialsforangular.com/2020/12/12/subject-vs-replaysubject-vs-behaviorsubject/

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 MindingData