Angular 4+ ngOnDestroy () in Betrieb - beobachtbar zerstören

102

In einer Winkelanwendung haben wir einen ngOnDestroy()Lebenszyklus-Hook für eine Komponente / Direktive und verwenden diesen Hook, um die Observablen abzumelden.

Ich möchte beobachtbare Objekte löschen / zerstören, die in einem @injectable()Dienst erstellt wurden. Ich habe einige Posts gesehen, die das sagtenngOnDestroy() sie auch in einem Dienst verwendet werden können.

Aber ist es eine gute Praxis und nur eine Möglichkeit, dies zu tun, und wann wird es aufgerufen? jemand bitte klären.

mperle
quelle

Antworten:

119

Der OnDestroy- Lebenszyklus-Hook ist bei Anbietern verfügbar. Laut den Dokumenten:

Lifecycle-Hook, der aufgerufen wird, wenn eine Direktive, eine Pipe oder ein Dienst zerstört wird.

Hier ist ein Beispiel :

@Injectable()
class Service implements OnDestroy {
  ngOnDestroy() {
    console.log('Service destroy')
  }
}

@Component({
  selector: 'foo',
  template: `foo`,
  providers: [Service]
})
export class Foo implements OnDestroy {
  constructor(service: Service) {}

  ngOnDestroy() {
    console.log('foo destroy')
  }
}

@Component({
  selector: 'my-app',
  template: `<foo *ngIf="isFoo"></foo>`,
})
export class App {
  isFoo = true;

  constructor() {
    setTimeout(() => {
        this.isFoo = false;
    }, 1000)
  }
}

Beachten Sie, dass es sich bei dem obigen Code Serviceum eine Instanz handelt, die zur FooKomponente gehört, sodass sie bei Zerstörung Foozerstört werden kann.

Für Anbieter, die zum Root-Injector gehören, geschieht dies bei der Zerstörung von Anwendungen. Dies ist hilfreich, um Speicherverluste bei mehreren Bootstraps, dh bei Tests, zu vermeiden.

Wenn ein Anbieter vom übergeordneten Injektor eine untergeordnete Komponente abonniert hat, wird diese bei der Zerstörung der Komponente nicht zerstört. Dies liegt in der Verantwortung der Komponente, sich in der Komponente abzumelden ngOnDestroy(wie in einer anderen Antwort erläutert).

Estus Flask
quelle
Nein class Service implements OnDestroy? Und was denkst du, wenn dies aufgerufen wird, wenn der Service auf Modulebene bereitgestellt wird
Shumail
1
implements OnDestroybeeinflusst nichts, kann aber der Vollständigkeit halber hinzugefügt werden. Es wird aufgerufen, wenn ein Modul zerstört wird, wie z appModule.destroy(). Dies kann für mehrere App-Initialisierungen hilfreich sein.
Estus Flask
1
Ist eine Abmeldung für jede Komponente erforderlich, die Dienste verwendet?
Ali Abbaszade
2
Der Plunker funktionierte nicht für mich, daher hier eine StackBlitz-Version des Beispiels: stackblitz.com/edit/angular-mggk9b
compuguru
1
Ich hatte einige Probleme, das zu verstehen. Diese Diskussion hat mir jedoch geholfen, den Unterschied zwischen lokalen und globalen Diensten zu verstehen : stackoverflow.com/questions/50056446/… Ob Sie "bereinigen" müssen oder nicht, hängt vom Umfang Ihres Dienstes ab, denke ich.
Jasmin
25

Erstellen Sie eine Variable in Ihrem Service

subscriptions: Subscriptions[]=[];

Drücken Sie jedes Ihrer Abonnements auf das Array als

this.subscriptions.push(...)

Schreiben Sie eine dispose()Methode

dispose(){
this.subscriptions.forEach(subscription =>subscription.unsubscribe())

Rufen Sie diese Methode während ngOnDestroy von Ihrer Komponente aus auf

ngOnDestroy(){
   this.service.dispose();
 }
Aravind
quelle
Danke für Ihre Antwort. Haben wir eine Idee, wann dieses ngOnDestroy aufgerufen wird? ?
Mperle
Ja, es heißt, es ist ein Bereinigungsaufruf, bevor die Direktive oder Komponente zerstört wird. aber ich möchte nur verstehen, ob es auch für den Service gilt?
Mperle
Beim Entladen des Moduls werden keine Dienste gelöscht
Aravind
2
Lebenszyklus-Haken sind nicht anwendbar für@injectables
Aravind
@Aravind Ich bin mir nicht sicher, wann sie vorgestellt wurden, aber sie sind es .
Estus Flask
11

Ich bevorzuge dieses takeUntil(onDestroy$)Muster, das von Pipeline-Betreibern aktiviert wird. Ich mag es, dass dieses Muster prägnanter und sauberer ist und eindeutig die Absicht vermittelt, ein Abonnement bei Ausführung des zu löschenOnDestroy Lebenszyklus-Hooks zu löschen.

Dieses Muster funktioniert sowohl für Dienste als auch für Komponenten, die injizierte Observable abonnieren. Der folgende Skelettcode sollte Ihnen genügend Details geben, um das Muster in Ihren eigenen Dienst zu integrieren. Stellen Sie sich vor, Sie importieren einen Dienst namens InjectedService...

import { InjectedService } from 'where/it/lives';
import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class MyService implements OnDestroy {

  private onDestroy$ = new Subject<boolean>();

  constructor(
    private injectedService: InjectedService
  ) {
    // Subscribe to service, and automatically unsubscribe upon `ngOnDestroy`
    this.injectedService.observableThing().pipe(
      takeUntil(this.onDestroy$)
    ).subscribe(latestTask => {
      if (latestTask) {
        this.initializeDraftAllocations();
      }
    });
  }

  ngOnDestroy() {
    this.onDestroy$.next(true);
    this.onDestroy$.complete();
  }

Das Thema wann / wie man sich abmeldet wird hier ausführlich behandelt: Angular / RxJs Wann sollte ich mich von `Subscription` abmelden

Matthew Marichiba
quelle
5

Nur zur Klarstellung - Sie müssen nicht zerstören Observables sondern nur die Abonnements, die für sie abgeschlossen wurden.

Es scheint, als hätten andere darauf hingewiesen, dass Sie jetzt auch ngOnDestroymit Diensten arbeiten können. Link: https://angular.io/api/core/OnDestroy

Apeshev
quelle
1
Können Sie bitte näher darauf
eingehen
2

Vorsicht bei der Verwendung von Token

Bei dem Versuch, meine Anwendung so modular wie möglich zu gestalten, verwende ich häufig Provider-Token, um einen Dienst für eine Komponente bereitzustellen. Es scheint, dass diese ihre ngOnDestroyMethoden NICHT erhalten :-(

z.B.

export const PAYMENTPANEL_SERVICE = new InjectionToken<PaymentPanelService>('PAYMENTPANEL_SERVICE');

Mit einem Provider-Bereich in einer Komponente:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

Mein ShopPaymentPanelServicehat NICHT seinengOnDestroy Methode von aufgerufen, wenn die Komponente entsorgt wird. Ich habe das gerade auf die harte Tour herausgefunden!

Eine Problemumgehung besteht darin, den Service in Verbindung mit bereitzustellen useExisting.

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Als ich das tat, ngOnDisposewurde der wie erwartet aufgerufen.

Ich bin mir nicht sicher, ob dies ein Fehler ist oder nicht, aber sehr unerwartet.

Simon_Weaver
quelle
Toller Hinweis! Ich habe mich gefragt, warum es in meinem Fall nicht funktioniert hat (ich habe die abstrakte Klassenschnittstelle als Token für die konkrete Implementierung verwendet).
Andrei Sinitson