0

I have an implementation, that automatically unsubscribes from Observables when the component is destroyed using takeUntil. But it's annoying to implement the same code in many components.

I want to know if this could be simplified (I can't use async pipe because I need the emitted values in the Typescript component)

Here is my current implementation:

export class Component implements OnDestroy {
    _dstr = new Subject();

    data$: Observable<any> = this.store.select(Selector.getData);

    constructor(
        private store: Store<State>,
    ) {
        this.store.pipe(
            select(Selector.getOtherData),
            takeUntil(this._dstr),
        ).subscribe(data => {
            console.log('here is my data!', data)
        });
    }

    public ngOnDestroy(): void {
        this._dstr.next();
        this._dstr.complete();
    }

}
Florian Ludewig
  • 4,338
  • 11
  • 71
  • 137
  • One of the solution is to bind observable to view through async pipe which take cares of automatically unsubscribing. – jakubm Dec 09 '18 at 10:39
  • 1
    If you need the data in your typescript component, and not in the template, then you are not using the component correctly. You should try to never use `subscribe` and let everything be handled by the `async` pipe, and transform the data using the rxjs operators. If you however do need the data, you can always just select it once using `take(1)` or `first()`, which will unsubscribe automatically, because the observable completes immediately – Poul Kruijt Dec 09 '18 at 10:41
  • Try https://github.com/smnbbrv/ngx-rx-collector – smnbbrv Dec 09 '18 at 10:46

1 Answers1

1

you could collect all your subscriptions in an array and unsubscribe each in ngOnDestroy function. If you need this behaviour very often you could consider using an abstract class from which you extend all your components from which does this for you.

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription, Observable, of } from 'rxjs';

export abstract class BaseComponent implements OnDestroy{
  public subscriptions: Subscription[] = [];

  public ngOnDestroy(): void {
    console.log("destroyed");
    this.subscriptions.forEach(s => s.unsubscribe());
  }
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent {

}

let count: number = 0;
@Component(
  {
    selector: 'app-derived',
    template: 'Derived Class'
  }
)
export class DerivedComponent extends BaseComponent implements OnInit, OnDestroy {
  private store: Observable<any> = of(count++);
  constructor() {
    super();
  }
  public ngOnInit(): void {
    this.subscriptions.push( this.store.subscribe(data => console.log(data)) );
  }
}

Blitzstack demo: https://stackblitz.com/edit/angular-peyoac

A.Winnen
  • 1,680
  • 1
  • 7
  • 13
  • That's great! But I am not really why `super()` is needed? – Florian Ludewig Dec 09 '18 at 12:59
  • 1
    Any derived class must call the parent constructor. In my short example super is not really needed because it has no arguments. The typescript transpiler till call it under the hood for you. But as soon as you have some arguments in derived class' constructor you will need to call it manually. Otherwise you will receive an error – A.Winnen Dec 09 '18 at 13:05