mottosson
mottosson

Reputation: 3753

RxJS BehaviorSubject without initial value on subscribe

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!

Upvotes: 14

Views: 10247

Answers (4)

Phoder1
Phoder1

Reputation: 1

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;
            }
        }
    }
}

Upvotes: 0

jurev
jurev

Reputation: 1597

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
    }
  }
});

Upvotes: 1

JORGE ROJAS
JORGE ROJAS

Reputation: 41

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

Upvotes: 4

MoxxiManagarm
MoxxiManagarm

Reputation: 9124

I think there are several options:

  • Use Subject
  • Keep BehaviorSubject, but pipe with with skip(1) to ignore the first value

Upvotes: 11

Related Questions