9

I have an Angular application where I use RxJS BehaviorSubject to subscribe to a bool value that indicates an "in progress" status.

But I'm only interested in when the state changes and not the current state on subscription.

export class ProgressService {
  private InProgress$ = new BehaviorSubject<boolean>(false);

  constructor() {}

  public getInProgressStateSubject() {
    return this.InProgress$;
  }
}

...

this.progressService.getInProgressSubject().subscribe((inProgress: boolean) => {

  // This will be triggered in the moment of subscription as well as on state chages

  if (inProgress) {
    // Toggle on state
  } else {
    // Toggle off state
  }
});

I like how it works, I just don't want it to trigger on subscription.

Are there any other similar operators in RxJS that can help me, or can I do it in any other way?

Thanks!

mottosson
  • 3,283
  • 4
  • 35
  • 73
  • 4
    simply use plain old subject ? This supplies you with next() still. – Chund Nov 26 '19 at 13:54
  • 1
    Oh bother... Thank you! I believe this is what I'm looking for. For some reason I didn't think Subject had the next() method to trigger changes and didn't even try =P – mottosson Nov 26 '19 at 14:00
  • The big difference between a subject and a behavior subject is the behavior subject stores the last emitted value and new subscribers get the last emitted on subscribing. With a subject new subscribers don't get a value until the next time it emits. – Adrian Brand Nov 26 '19 at 20:31
  • Thanks @AdrianBrand for the explanation! – mottosson Nov 27 '19 at 07:13
  • 2
    Does this answer your question? [RxJS: How to not subscribe to initial value and/or undefined?](https://stackoverflow.com/questions/28314882/rxjs-how-to-not-subscribe-to-initial-value-and-or-undefined) – Stephanie Mar 02 '22 at 15:09

4 Answers4

7

I think there are several options:

  • Use Subject
  • Keep BehaviorSubject, but pipe with with skip(1) to ignore the first value
MoxxiManagarm
  • 8,735
  • 3
  • 14
  • 43
  • 1
    skip(1) is not always a good option because you can inadvertently skip a valid value if you subscribed to BehaviorSubject after another service already published a valid value. – jurev Mar 17 '23 at 14:12
2

You can use ReplaySubject with bufferSize 1 and they only have 1 value and the last.

JORGE ROJAS
  • 21
  • 1
  • 1
1

Maybe you can use undefined as initial value of BehaviorSubject, and ignore it in your subscribe method:

export class ProgressService {
  private InProgress$ = new BehaviorSubject<boolean | undefined>(undefined);

  constructor() {}

  public getInProgressStateSubject() {
    return this.InProgress$;
  }
}

...

this.progressService.getInProgressSubject().subscribe((inProgress?: boolean) => {
  if (inProgress !== undefined) {
    // This won't be triggered on BehaviorSubject initialization but only when state is boolean 
    if (inProgress) {
      // Toggle on state
    } else {
      // Toggle off state
    }
  }
});
jurev
  • 1,457
  • 1
  • 14
  • 22
0

I implemented my own solution on C#: Please notice that I'm pretty sure it's not thread safe

public static class FutureObservable
{
    public static IObservable<T> Future<T>(this IObservable<T> observable)
    {
        if (observable is FutureObservableWrapper<T> future)
            return future;

        return new FutureObservableWrapper<T>(observable);
    }
    
    private class FutureObservableWrapper<T> : IObservable<T>
    {
        private readonly IObservable<T> _source;

        public FutureObservableWrapper(IObservable<T> source)
        {
            _source = source;
        }

        public IDisposable Subscribe(IObserver<T> observer)
        {
            var connectable = new ConnectableObserver(observer);
            var disposable = _source.Subscribe(connectable);
            connectable.Connect();
            return disposable;
        }
        
        private class ConnectableObserver : IObserver<T>
        {
            private readonly IObserver<T> _observer;
            private bool _connected;

            public ConnectableObserver(IObserver<T> observer)
            {
                _observer = observer;
            }

            public void OnCompleted() => _observer.OnCompleted();

            public void OnError(Exception error) => _observer.OnError(error);

            public void OnNext(T value)
            {
                if (!_connected)
                    return;
                
                _observer.OnNext(value);
            }

            public void Connect()
            {
                _connected = true;
            }
        }
    }
}
Phoder1
  • 1
  • 2
  • Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. **Would you kindly [edit] your answer to include additional details for the benefit of the community?** – Jeremy Caney Aug 23 '23 at 06:39