17

I have shareService and subscribe that is in another components :

import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs/Subscription';
@Component({
  selector: 'my-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})
export class AstronautComponent implements OnDestroy{
  @Input() astronaut: string;
  mission = "<no mission announced>";
  confirmed = false;
  announced = false;
  subscription:Subscription;

  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    })




  }
  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }
  ngOnDestroy(){
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();

  }
}

I want to know if I have 2 subscribers in my constructor, how to unsubscribe twice subscribers in ngDestroy? I should use subscription2:Subscription;?and in ngDestroy this.subscription2.unsubscribe();? this is true?

3 Answers3

34

You can collect subscriptions that you want to unsubscribe at once in ngOnDestroy() in an array

export class AstronautComponent implements OnDestroy{
  @Input() astronaut: string;
  mission = "<no mission announced>";
  confirmed = false;
  announced = false;
  subscriptions:Subscription[] = [];

  constructor(private missionService: MissionService) {
    this.subscriptions.push(missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    }));

    this.subscriptions.push(fooService.fooObservable$.subscribe(
      ...
    }));
  }

  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }

  ngOnDestroy(){
    // prevent memory leak when component destroyed
    this.subscriptions.forEach(s => s.unsubscribe());
  }
}
Rajshri Mohan K S
  • 1,557
  • 17
  • 30
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
20

A Subscription can hold child subscriptions and safely unsubscribe them all. This method handles possible errors (e.g. if any child subscriptions are null). Also, if any subscriptions are added after ngOnDestroy is called, they will be immediately unsubscribed.

It should be noted that Subscription.add() returns the added subscription. So chaining add() like subManager.add(sub1).add(sub2) will add sub2 to sub1. This will be an issue if sub1 is unsubscribed at an earlier time, as it will also unsubscribe from sub2.

import { OnDestroy } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { Subscription } from "rxjs/Subscription";

export class MyComponent implements OnDestroy {
    ob1: Observable<number>;
    ob2: Observable<number>;
    subManager = new Subscription();

    constructor() {
        this.ob1 = Observable.interval(1000);
        this.ob2 = Observable.interval(2000);

        let sub1 = this.ob1.subscribe(val => console.log(val));
        let sub2 = this.ob2.subscribe(val => console.log(val));

        // DO NOT CHAIN add() - SEE ABOVE
        this.subManager.add(sub1);
        this.subManager.add(sub2);
    }

    ngOnDestroy() {
        this.subManager.unsubscribe();
    }
}

From the documentation:

Additionally, subscriptions may be grouped together through the add() method, which will attach a child Subscription to the current Subscription. When a Subscription is unsubscribed, all its children (and its grandchildren) will be unsubscribed as well.

...

If this subscription is already in an closed state, the passed tear down logic will be executed immediately.

makman99
  • 1,045
  • 14
  • 18
  • Yeah, my question was for angular beta –  Aug 24 '17 at 14:08
  • Is there an advantage to doing this this way versus an array of subscriptions like in the accepted answer? – Mathachew Feb 21 '19 at 22:54
  • Yes, I listed the advantages I could think of - see top paragraph. The same null and closed state checks could be done with the array of subscriptions, but that is exactly what a Subscription is. There is no point redoing it all in your component. – makman99 Feb 22 '19 at 14:15
0

In my opinion, more elegant and universal (does not matter how many subscription you have) is to use takeUntil operators from rxjs package.

import { OnDestroy } from "@angular/core";
import { Subject, Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

export class MyComponent implements OnDestroy {

    ob1: any;
    ob2: any;

    private _endSubs: Subject<any> = new Subject();
    private endSubs$ = this._endSubs.asObservable();

    constructor() {
        this.ob1 = Observable.interval(1000);
        this.ob2 = Observable.interval(2000);

        let sub1 = this.ob1.pipe(takeUntil(this.endSubs$)).subscribe(val => console.log(val));
        let sub2 = this.ob2.pipe(takeUntil(this.endSubs$)).subscribe(val => console.log(val));

        .... (more subscriptions here)
    }

    ngOnDestroy() {
        this._endSubs.next();
    }
}
jspassov
  • 791
  • 7
  • 11