Jak anulować subskrypcję w Angular2

81

Jak anulować subskrypcję w Angular2? Wydaje się, że RxJS ma metodę usuwania, ale nie mogę dowiedzieć się, jak uzyskać do niej dostęp. Mam więc kod, który ma dostęp do EventEmittera i subskrybuje go, na przykład:

var mySubscription = someEventEmitter.subscribe(
    (val) => {
        console.log('Received:', val);
    },
    (err) => {
        console.log('Received error:', err);
    },
    () => {
        console.log('Completed');
    }
);

Jak mogę mySubscriptionanulować subskrypcję?

Michael Oryl
źródło
2
FYI - jeśli masz zamiar robić rzeczy reaktywne, użyj tematu, a nie EventEmittera Angulara - nie ma gwarancji, że pozostanie on nadklasą podmiotu. Użyj EventEmitter tylko dla zdarzeń @Output.
robwormald

Odpowiedzi:

121

Chcesz się wypisać?

mySubscription.unsubscribe();
kendaleiv
źródło
4
Dobry panie. Przysięgałem, że już tego próbowałem. Spojrzałem na źródło RxJS i wydawało się, że to było to. Musiałem mieć inny błąd powodujący problemy. Dzięki.
Michael Oryl
1
to działa dobrze, ale jestem ciekawy, jaki jest typ mySubscriptionw TypeScript. Chcę uniknąć pisania mySubscription: anyna zajęciach.
paradite
11
@paradite import { Subscription } from "rxjs";i do wypisania się if (!mySubscription.closed) { mySubscription.unsubscribe(); }.
Llyle
9
import { Subscription } from 'rxjs/Subscription';pomoże zmniejszyć rozmiar paczki
paul
50

Myślałem, że włożyłem też moje dwa centy. Używam tego wzoru:

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

@Component({
    selector: 'my-component',
    templateUrl: 'my.component.html'
})
export class MyComponent implements OnInit, OnDestroy {

    private subscriptions: Array<Subscription> = [];

    public ngOnInit(): void {
        this.subscriptions.push(this.someService.change.subscribe(() => {
            [...]
        }));

        this.subscriptions.push(this.someOtherService.select.subscribe(() => {
            [...]
        }));
    }

    public ngOnDestroy(): void {
        this.subscriptions.forEach((subscription: Subscription) => {
            subscription.unsubscribe();
        });
    }
}

EDYTOWAĆ

Przeczytałem dokumentację pewnego dnia i znalazłem bardziej zalecany wzór:

ReactiveX / RxJS / Subskrypcja

Plusy:

Zarządza wewnętrznie nowymi subskrypcjami i dodaje kilka zgrabnych kontroli. Wolałabym tę metodę w funkcji :).

Cons:

Nie jest w 100% jasne, jaki jest przepływ kodu i jaki ma to wpływ na subskrypcje. Nie jest też jasne (wystarczy spojrzeć na kod), jak radzi sobie z zamkniętymi subskrypcjami i czy wszystkie subskrypcje są zamykane w przypadku wywołania anulowania subskrypcji.

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

@Component({
    selector: 'my-component',
    templateUrl: 'my.component.html'
})
export class MyComponent implements OnInit, OnDestroy {

    private subscription: Subscription = new Subscription();

    public ngOnInit(): void {
        this.subscription.add(this.someService.change.subscribe(() => {
            [...]
        }));

        this.subscription.add(this.someOtherService.select.subscribe(() => {
            [...]
        }));
    }

    public ngOnDestroy(): void {
        /*
         * magic kicks in here: All subscriptions which were added
         * with "subscription.add" are canceled too!
         */
        this.subscription.unsubscribe();
    }
}
Nightking
źródło
2
Ja też tego używam, ładnie się skaluje, im więcej masz subskrypcji w komponencie.
weltschmerz
1
Żadnej „magii” - jest to zgodne z projektem: „Subskrypcje można również łączyć, aby wywołanie funkcji unsubscribe () jednej subskrypcji mogło anulować wiele subskrypcji. Możesz to zrobić przez„ dodanie ”jednej subskrypcji do innej:„ github. com / ReactiveX / rxjs / blob / master / doc / subscription.md
Meetai.com
Jest jeszcze jedno jasne podejście, które mogłoby Cię zainteresować. Korzystanie z takeWhileoperatora. Podejście opisane tutaj: brianflove.com/2016/12/11/anguar-2-unsubscribe-observables
vdshb
Sprawdź alternatywne rozwiązanie , jakim jest takeUntil. Dyskusja jest bogata i interesująca, biorąc pod uwagę różne opcje.
Alex Klaus,
8

EDYCJA: Nie dotyczy to RxJS 5, którego używa angular2.

Myślałem, że szukasz metody usuwania Jednorazowe .

metoda subscribe zwraca Disposable ( link )

Nie mogę znaleźć tego bardziej szczegółowo w dokumentacji, ale to działa ( jsbin ):

var observable = Rx.Observable.interval(100);

var subscription = observable.subscribe(function(value) {
   console.log(value);
});

setTimeout(function() {
  subscription.dispose();           
}, 1000)

Co dziwne, anulowanie subskrypcji wydaje się działać dla Ciebie, podczas gdy nie działa dla mnie ...

Niklas Fasching
źródło
Prawdopodobnie używasz różnych wersji. Angular2 jest na Rxjs5, prawda?
user3743222
Tak, Angular 2 używa RxJS 5.
Michael Oryl
1
Otóż ​​to. Można znaleźć tutaj . Zmiana została wprowadzona zgodnie ze specyfikacją obserwowalną ES7
Niklas Fasching
4

Zbyt wiele różnych wyjaśnień anulowania subskrypcji na Observables dla ng2 zajęło mi wieki, aby znaleźć właściwą odpowiedź. Poniżej znajduje się działający przykład (próbowałem zdusić ruch myszy).

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

@Injectable()
export class MyClass implements OnDestroy {
  
  mouseSubscription: Subscription; //Set a variable for your subscription
  
  myFunct() {
    // I'm trying to throttle mousemove
    const eachSecond$ = Observable.timer(0, 1000);
    const mouseMove$ = Observable.fromEvent<MouseEvent>(document, 'mousemove');
    const mouseMoveEachSecond$ = mouseMove$.sample(eachSecond$);
    
    this.mouseSubscription = mouseMoveEachSecond$.subscribe(() => this.doSomethingElse());
  }

  doSomethingElse() {
    console.log("mouse moved");
  }
  
  stopNow() {
    this.mouseSubscription.unsubscribe();
  }
  
  ngOnDestroy() {
    this.mouseSubscription.unsubscribe();
  }
  
}

Joe Keene
źródło
2
ngOnDestroy(){
   mySubscription.unsubscribe();
}

Preferuj anulowanie subskrypcji rxjs anulowanie subskrypcji podczas niszczenia komponentu, tj. Usuwanie z DOM w celu uniknięcia niepotrzebnych wycieków pamięci

Prithvi Uppalapati
źródło
2

Osobiście wolę używać Przedmiotu, aby zamknąć wszystkie subskrypcje, które może mieć komponent na etapie niszczenia cyklu życia, co można osiągnąć w ten sposób:

import { Component} from '@angular/core';
import { Subject } from "rxjs/Rx";

@Component({
  selector:    'some-class-app',
  templateUrl: './someClass.component.html',
  providers:   []
})

export class SomeClass {  

  private ngUnsubscribe: Subject<void> = new Subject<void>(); //This subject will tell every subscriptions to stop when the component is destroyed.

  //**********
  constructor() {}

  ngOnInit() {

    this.http.post( "SomeUrl.com", {}, null ).map( response => {

      console.log( "Yay." );

    }).takeUntil( this.ngUnsubscribe ).subscribe(); //This is where you tell the subscription to stop whenever the component will be destroyed.
  }

  ngOnDestroy() {

    //This is where we close any active subscription.
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }
}
Jahrenski
źródło
2

Zalecanym podejściem jest użycie operatorów RxJS, takich jak operator takeUntil . Poniżej znajduje się fragment kodu pokazujący, jak go używać: -

import { Component, OnInit, OnDestroy } from '@angular/core';
import { interval, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
    private ngUnsubscribe = new Subject();

    constructor() { }

    ngOnInit() {
        var observable1 = interval(1000);
        var observable2 = interval(2000);

        observable1.pipe(takeUntil(this.ngUnsubscribe)).subscribe(x => console.log('observable1: ' + x));
        observable2.pipe(takeUntil(this.ngUnsubscribe)).subscribe(x => console.log('observable2: ' + x));
    }

    ngOnDestroy() {
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
    }
}

Szczegółowe wyjaśnienie tematu można znaleźć tutaj

Yatharth Varshney
źródło
dlaczego musimy wywołać ngUnsubscribe.next (); w metodzie ngOnDestroy ()?
Biswa Bandhu Bhandary
1

Posługiwać się

if(mySubscription){
  mySubscription.unsubscribe();
}
Anshul
źródło
1
Właściwie powinno byćif (!mySubscription.closed) { mySubscription.unsubscribe(); }
Llyle
-1
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { SomeAPIService } from '../some_file/someAPIService.service.ts

@Component({
  templateUrl: './your_Page.html',
  styleUrls: ['./your_Styles.scss']
})

export class (your class) implements OnInit, OnDestroy {
   // This is a subject variable at it simplest form 
     private unsubscribe$ = new Subject<void>();

     constructor (private someAPIService : SomeAPIService) {}
   
     ngOnit(): void { 
       this.someAPIService.getTODOlist({id:1})
        .pipe(takeUntil(this.unsubscribe$))
         .subscribe((value: SomeVariable) => {
         // What ever value you need is SomeVariable
      },)
    }


     ngOnDestroy(): void {
    // clears all, page subscriptions 
      this.unsubscribe$.next();
      this.unsubscribe$.complete();
     }
`}
J Crain
źródło