web-dev-qa-db-fra.com

Angular 4+ ngOnDestroy () en service - détruit observable

Dans une application angulaire, nous avons un hook ngOnDestroy() lifecycle pour un composant/une directive et nous utilisons ce hook pour désabonner les observables.

Je souhaite effacer/supprimer les éléments observables créés dans un service @injectable() .J'ai vu certains messages affirmant que ngOnDestroy() peut également être utilisé dans un service.

Mais, est-ce une bonne pratique et la seule façon de le faire et quand sera-t-il appelé?.

34
mperle

OnDestroy Lifecycle hook est disponible chez les fournisseurs . Selon la documentation:

Le hook Lifecycle appelé lorsqu'une directive, un canal ou un service est détruit.

Voici un exemple :

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

@Component({
  selector: 'foo',
  template: `foo`,
  providers: [Service]
})
export class Foo {
  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)
  }
}

Notez que, dans le code ci-dessus, Service est une instance qui appartient au composant Foo. Elle peut donc être détruite lorsque Foo est détruit.

Pour les fournisseurs appartenant à l'injecteur racine, cela se produira lors de la destruction de l'application, ce qui est utile pour éviter les fuites de mémoire lors de l'amorçage multiple, c'est-à-dire lors des tests.

Lorsqu'un fournisseur de l'injecteur parent est abonné dans le composant enfant, il ne sera pas détruit lors de la destruction du composant. Il incombe au composant de se désabonner du composant ngOnDestroy (comme l'explique une autre réponse).

42
estus

Créer une variable dans votre service

subscriptions: Subscriptions[]=[];

Poussez chacun de vos abonnés au tableau comme

this.subscriptions.Push(...)

Écrire une méthode dispose()

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

Appelez cette méthode à partir de votre composant pendant ngOnDestroy

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

Juste pour clarifier les choses: vous n'avez pas besoin de détruire Observables, mais uniquement les abonnements qui leur ont été attribués.

Il semblerait que d’autres personnes aient fait remarquer que vous pouvez maintenant utiliser ngOnDestroy avec des services également. Lien: https://angular.io/api/core/OnDestroy

5
apeshev

Attention si vous utilisez des jetons

En essayant de rendre mon application aussi modulaire que possible, j'utilise souvent des jetons de fournisseur pour fournir un service à un composant. Il semble que ceux-ci ne reçoivent PAS leurs méthodes ngOnDestroy appelées :-(

par exemple.

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

Avec une section fournisseur dans un composant:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

Mon ShopPaymentPanelService n'a PAS sa méthode ngOnDestroy appelée lorsque le composant est supprimé. Je viens de découvrir cela à la dure!

Une solution de contournement consiste à fournir le service conjointement avec useExisting.

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Quand j'ai fait cela, la ngOnDispose a été appelée comme prévu.

Pas sûr que ce soit un bug ou non mais très inattendu.

0
Simon_Weaver

Je préfère ce modèle takeUntil(onDestroy$) activé par les opérateurs pouvant être raccordés. J'aime que ce modèle soit plus concis, plus propre et indique clairement l'intention de supprimer un abonnement lors de l'exécution du hook OnDestroy lifecycle.

Ce modèle fonctionne pour les services ainsi que pour les composants abonnés aux observables injectés. Le code squelette ci-dessous devrait vous donner suffisamment de détails pour intégrer le modèle à votre propre service. Imaginez que vous importiez un service appelé 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();
  }

Le sujet de quand/comment se désabonner est couvert en détail ici: Angular/RxJs Quand dois-je me désabonner de `Subscription`

0
Matthew Marichiba