2

In RxJS I need to execute some code after all subscriptions for a given observable have been executed. I need to do this after every single next notification that fires and after all subscriber functions have completed (not only once when the observable completes).

In other words, I need an operator like tap() that runs after all subscribers have been executed. Is there a way?

Example:

I'm developing a library of UI widgets (say some kind of extensions to those already provided by angular material). One of those widgets has to accomplish the following:

  1. provide a button,
  2. when the button is clicked, display a spinner inside the button,
  3. yield the control to who is using the widget to do some work (e.g. write inside a database),
  4. when the work is done hide the spinner inside the button.

Assuming to use an Observable to achieve the above, I would like to be able to do something like (I'm using typescript in an Angular 10 project if it could help to provide a solution):

const eventEmitter = new EventEmitter<any>();

const observable = eventEmitter.asObservable().pipe(
  tap(() => ...show the spinner...), // OK, no problem
  someOperator(() => ...hide the spinner when all subscriber functions complete...) // Which operator can I use?
);

observable.subscribe(() => console.log('First consumer'));
observable.subscribe(() => console.log('Second consumer'));

eventEmitter.emit('DATA');

The desired sequence of events when the button is clicked is:

  1. The event is emitted with 'DATA' as payload,
  2. The spinner inside the button is displayed,
  3. 'First consumer' is logged in the console,
  4. 'Second consumer' is logged in the console,
  5. The spinner inside the button is removed.

BONUS QUESTION: how could I run some code after the execution of every single subscriber?

Sirion
  • 804
  • 1
  • 11
  • 33

1 Answers1

-1

If I understand correctly, this functionality should be wrapped within a separate component or as a directive. The "observable" that you would need to listen to in the template to show and hide the spinner is the loading state controlled by BehaviorSubject. So the template for the button should look like this:

<button (click)="buttonClicked()">New UI Element <span *ngIf="loading | async">Spinner</span></button>

You can use event bindings in the template to trigger the function responsible for the button click logic.

The component logic should look like this using BehaviorSubject.

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

...

buttonClicked() {
    this.loading.next(true);

    // Perform some logic

    this.loading.next(false);
}
jamesmallred
  • 206
  • 1
  • 6