user5738822
user5738822

Reputation:

how to unsubscribe several subscriber in angular 2

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?

Upvotes: 17

Views: 23242

Answers (3)

jspassov
jspassov

Reputation: 811

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

Upvotes: 0

makman99
makman99

Reputation: 1094

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.

Upvotes: 21

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 658067

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

Upvotes: 34

Related Questions