Angular 4+ ngOnDestroy () in service - distruggi osservabile


104

In un'applicazione angolare abbiamo un ngOnDestroy()hook del ciclo di vita per un componente / direttiva e utilizziamo questo hook per annullare la sottoscrizione delle osservabili.

Voglio cancellare / destory osservabili che vengono creati in un @injectable()servizio. Ho visto alcuni post che dicevano che ngOnDestroy()può essere utilizzato anche in un servizio.

Ma è una buona pratica e l'unico modo per farlo e quando verrà chiamato? qualcuno per favore chiarisca.

Risposte:


121

L' hook del ciclo di vita di OnDestroy è disponibile nei provider. Secondo i documenti:

Hook del ciclo di vita che viene chiamato quando una direttiva, una pipe o un servizio viene distrutto.

Ecco un esempio :

@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)
  }
}

Si noti che nel codice sopra Servicec'è un'istanza che appartiene al Foocomponente, quindi può essere distrutta quando Fooviene distrutta.

Per i provider che appartengono all'iniettore di root, ciò accadrà durante la distruzione dell'applicazione, questo è utile per evitare perdite di memoria con più bootstraps, cioè nei test.

Quando un provider dall'iniettore principale viene sottoscritto nel componente figlio, non verrà distrutto durante la distruzione del componente, questa è responsabilità del componente annullare l'iscrizione al componente ngOnDestroy(come spiega un'altra risposta).


No class Service implements OnDestroy? E cosa ne pensi quando viene chiamato se il servizio viene fornito a livello di modulo
Shumail

1
implements OnDestroynon influisce su nulla ma può essere aggiunto per completezza. Sarà chiamato quando un modulo viene distrutto, come appModule.destroy(). Questo può essere utile per più inizializzazioni di app.
Estus Flask

1
è necessario annullare l'iscrizione per ogni componente che utilizza i servizi?
Ali Abbaszade

2
Il Plunker non funzionava per me, quindi ecco una versione StackBlitz dell'esempio: stackblitz.com/edit/angular-mggk9b
compuguru

1
Ho avuto qualche difficoltà a capirlo. Ma questa discussione mi ha aiutato a capire la differenza tra servizi locali e globali: stackoverflow.com/questions/50056446/… Se devi "ripulire" o meno dipende dall'ambito del tuo servizio, penso.
Jasmin

27

Crea una variabile nel tuo servizio

subscriptions: Subscriptions[]=[];

Esegui il push di ciascuna delle tue iscrizioni all'array come

this.subscriptions.push(...)

Scrivi un dispose()metodo

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

Chiama questo metodo dal tuo componente durante ngOnDestroy

ngOnDestroy(){
   this.service.dispose();
 }

Grazie per la risposta. Abbiamo idea di quando verrà chiamato questo ngOnDestroy. ?
mperle

sì, dice che è una chiamata di pulizia prima che la direttiva o il componente venga distrutto. ma voglio solo capire se è applicabile anche al servizio?
mperle

Nessun servizio verrà cancellato quando il modulo viene scaricato
Aravind

2
i ganci del ciclo di vita non sono applicabili per@injectables
Aravind

@Aravind non sono sicuro di quando sono stati presentati ma lo sono .
Estus Flask

11

Preferisco questo takeUntil(onDestroy$)modello abilitato dagli operatori pipable. Mi piace che questo modello sia più conciso, più pulito e trasmetta chiaramente l'intento di terminare un abbonamento all'esecuzione OnDestroydell'hook del ciclo di vita.

Questo modello funziona sia per i servizi che per i componenti che sottoscrivono osservabili iniettati. Il codice dello scheletro di seguito dovrebbe fornire dettagli sufficienti per integrare il modello nel tuo servizio. Immagina di importare un servizio chiamato 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();
  }

L'argomento di quando / come annullare l'iscrizione è trattato ampiamente qui: Angular / RxJs Quando dovrei cancellarmi da `Subscription`


5

Giusto per chiarire: non è necessario distruggere Observablesma solo gli abbonamenti effettuati a loro.

Sembra che altri abbiano sottolineato che ora puoi utilizzare anche ngOnDestroycon i servizi. Collegamento: https://angular.io/api/core/OnDestroy


1
Puoi approfondire di più su di esso
Aravind

2

Attenzione se si utilizzano gettoni

Nel tentativo di rendere la mia applicazione il più modulare possibile, userò spesso i token del provider per fornire un servizio a un componente. Sembra che questi NON ottengano i loro ngOnDestroymetodi chiamati :-(

per esempio.

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

Con una sezione provider in un componente:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

My ShopPaymentPanelServiceNON ha il suo ngOnDestroymetodo chiamato quando il componente viene eliminato. L'ho appena scoperto nel modo più duro!

Una soluzione alternativa consiste nel fornire il servizio insieme a useExisting.

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Quando l'ho fatto, è ngOnDisposestato chiamato come previsto.

Non sono sicuro che si tratti di un bug o meno, ma molto inaspettato.


Ottimo suggerimento! Mi chiedevo perché non funzionasse nel mio caso (stavo usando l'interfaccia di classe astratta come token per l'implementazione concreta).
Andrei Sinitson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.