132 votes

Angular 4+ ngOnDestroy() en service - détruire l'observable

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

Je veux effacer/détruire les observables qui sont créées dans une @injectable() service. J'ai vu des messages disant 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é ? Quelqu'un peut m'éclairer.

156voto

estus Points 5252

OnDestroy Le hook de cycle de vie est disponible dans les fournisseurs. Selon les docs :

Crochet de cycle de vie qui est appelé lorsqu'une directive, un tuyau 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 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)
  }
}

Remarquez que dans le code ci-dessus Service est une instance qui appartient à Foo afin qu'il puisse être détruit lorsque Foo est détruit.

Pour les fournisseurs qui appartiennent à l'injecteur de racine, cela se produira lors de la destruction de l'application, ce qui est utile pour éviter les fuites de mémoire avec des amorçages multiples, par exemple dans les tests.

Lorsqu'un fournisseur de l'injecteur parent est inscrit dans le composant enfant, il ne sera pas détruit lors de la destruction du composant, c'est la responsabilité du composant de le désinscrire dans le composant. ngOnDestroy (comme l'explique une autre réponse).

0 votes

Non class Service implements OnDestroy ? Et que pensez-vous lorsque cela est demandé si le service est fourni au niveau du module ?

1 votes

implements OnDestroy n'affecte rien mais peut être ajouté pour être complet. Elle sera appelée lorsqu'un module est détruit, comme appModule.destroy() . Cela peut être utile pour l'initialisation de plusieurs applications.

1 votes

Le désabonnement est-il nécessaire pour chaque composant qui utilise des services ?

32voto

Aravind Points 21523

Créez une variable dans votre service

subscriptions: Subscriptions[]=[];

Poussez chacun de vos souscriptions dans le tableau comme

this.subscriptions.push(...)

Écrire un dispose() méthode

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

Appelez cette méthode depuis votre composant pendant le ngOnDestroy.

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

0 votes

Merci pour votre réponse. Avons-nous une idée du moment où ce ngOnDestroy sera appelé ? ?

0 votes

Oui, il est dit que c'est un appel de nettoyage avant que la directive ou le composant ne soit détruit. mais je veux juste comprendre si c'est applicable pour le service aussi ?

0 votes

Aucun service ne sera effacé lorsque le module sera déchargé.

18voto

Matthew Marichiba Points 954

Je préfère ceci takeUntil(onDestroy$) activé par les opérateurs pipables. J'aime que ce modèle soit plus concis, plus propre, et qu'il exprime clairement l'intention de tuer un abonnement lors de l'exécution de la commande OnDestroy le crochet du cycle de vie.

Ce modèle fonctionne aussi bien pour les services que pour les composants s'abonnant à des observables injectés. Le squelette de code ci-dessous devrait vous donner suffisamment de détails pour intégrer ce modèle dans votre propre service. Imaginez que vous importez 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();
  }

La question de savoir quand et comment se désabonner est traitée en détail ici : Angular/RxJs Quand dois-je me désabonner de `Subscription` ?

5voto

apeshev Points 81

Juste pour clarifier - vous n'avez pas besoin de détruire Observables mais seulement les souscriptions qui leur sont faites.

Il semble que d'autres ont souligné que vous êtes maintenant en mesure d'utiliser ngOnDestroy avec des services également. Lien : https://angular.io/api/core/OnDestroy

2 votes

Pouvez-vous nous en dire plus ?

3voto

Simon_Weaver Points 31141

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 ces derniers n'obtiennent PAS leur ngOnDestroy les méthodes ont été appelées :-(

eg.

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 son ngOnDestroy appelée lorsque le composant est éliminé. Je viens de le découvrir à la dure !

Une solution de contournement est de fournir le service en conjonction avec useExisting .

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Quand j'ai fait ça, le ngOnDispose a été appelé comme prévu.

Je ne sais pas si c'est un bogue ou non, mais c'est très inattendu.

0 votes

Excellent conseil ! Je me demandais pourquoi cela ne fonctionnait pas dans mon cas (j'utilisais une classe-interface abstraite comme jeton pour une implémentation concrète).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X