Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJS: takeUntil() Angular component's ngOnDestroy()

tl;dr: Basically I want to marry Angular's ngOnDestroy with the Rxjs takeUntil() operator. -- is that possible?

I have an Angular component that opens several Rxjs subscriptions. These need to be closed when the component is destroyed.

A simple solution for this would be:

class myComponent {    private subscriptionA;   private subscriptionB;   private subscriptionC;    constructor(     private serviceA: ServiceA,     private serviceB: ServiceB,     private serviceC: ServiceC) {}    ngOnInit() {     this.subscriptionA = this.serviceA.subscribe(...);     this.subscriptionB = this.serviceB.subscribe(...);     this.subscriptionC = this.serviceC.subscribe(...);   }    ngOnDestroy() {     this.subscriptionA.unsubscribe();     this.subscriptionB.unsubscribe();     this.subscriptionC.unsubscribe();   }  } 

This works, but it's a bit redundant. I especially don't like that - The unsubscribe() is somewhere else, so you gotta remember that these are linked. - The component state is polluted with the subscription.

I would much prefer using the takeUntil() operator or something similar, to make it look like this:

class myComponent {    constructor(     private serviceA: ServiceA,     private serviceB: ServiceB,     private serviceC: ServiceC) {}    ngOnInit() {     const destroy = Observable.fromEvent(???).first();     this.subscriptionA = this.serviceA.subscribe(...).takeUntil(destroy);     this.subscriptionB = this.serviceB.subscribe(...).takeUntil(destroy);     this.subscriptionC = this.serviceC.subscribe(...).takeUntil(destroy);   }  } 

Is there a destroy event or something similar that would let me use takeUntil() or another way to simplify the component architecture like that? I realize I could create an event myself in the constructor or something that gets triggered within ngOnDestroy() but that would in the end not make things that much simpler to read.

like image 843
marius Avatar asked Feb 27 '17 16:02

marius


People also ask

What is takeUntil in RXJS?

takeUntil passes values from the source observable to the observer (mirroring) until a provided observable known as the notifier emits its first value. The operator subscribes to the source observable and begins mirroring the source Observable. It also subscribes to the notifier observable.

What is the use of takeUntil in angular?

The takeUntil operator is used to automatically unsubscribe from an observable. takeUntil begins mirroring the source Observable. It also monitors a second Observable, notifier that you provide. If the notifier emits a value, the output Observable stops mirroring the source Observable and completes.

Does takeUntil unsubscribe?

There is also a better way to unsubscribe from or complete Observables by using the takeUntil() operator. The takeUntil() operator emits the values emitted by the source Observable until a notifier Observable emits a value.

What is the difference between takeWhile and takeUntil RXJS operator?

The takeUntil(notifier) keeps emitting the values until it is notified to stop. takeWhile(predicate) emits the value while values satisfy the predicate.


1 Answers

You could leverage a ReplaySubject for that:

EDIT: Different since RxJS 6.x: Note the use of the pipe() method.

class myComponent {   private destroyed$: ReplaySubject<boolean> = new ReplaySubject(1);    constructor(     private serviceA: ServiceA,     private serviceB: ServiceB,     private serviceC: ServiceC) {}    ngOnInit() {     this.serviceA       .pipe(takeUntil(this.destroyed$))       .subscribe(...);     this.serviceB       .pipe(takeUntil(this.destroyed$))       .subscribe(...);     this.serviceC       .pipe(takeUntil(this.destroyed$))       .subscribe(...);   }    ngOnDestroy() {     this.destroyed$.next(true);     this.destroyed$.complete();   } } 

This is only valid for RxJS 5.x and older:

class myComponentOld {   private destroyed$: ReplaySubject<boolean> = new ReplaySubject(1);    constructor(private serviceA: ServiceA) {}    ngOnInit() {     this.serviceA       .takeUntil(this.destroyed$)       .subscribe(...);   }    ngOnDestroy() {     this.destroyed$.next(true);     this.destroyed$.complete();   } } 
like image 178
olsn Avatar answered Sep 21 '22 14:09

olsn