Angular 4+ ngOnDestroy () w służbie - zniszcz obserwowalne

104

W aplikacji kątowej mamy ngOnDestroy()hak cyklu życia dla komponentu / dyrektywy i używamy tego haka do anulowania subskrypcji obserwabli.

Chcę wyczyścić / zniszczyć obserwowalne, które są tworzone w @injectable()usłudze. Widziałem posty, w których napisano, że ngOnDestroy()można ich również użyć w usłudze.

Ale czy jest to dobra praktyka i jedyny sposób, aby to zrobić i kiedy zostanie wywołana? ktoś proszę o wyjaśnienie.

mperle
źródło

Odpowiedzi:

120

Hak cyklu życia OnDestroy jest dostępny w dostawcach. Według dokumentów:

Hak cyklu życia wywoływany w przypadku zniszczenia dyrektywy, potoku lub usługi.

Oto przykład :

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

Zauważ, że w powyższym kodzie Serviceznajduje się instancja należąca do Fookomponentu, więc może zostać zniszczona, gdy Foozostanie zniszczona.

W przypadku dostawców należących do wtryskiwacza root nastąpi to po zniszczeniu aplikacji, jest to pomocne w celu uniknięcia wycieków pamięci przy wielokrotnym ładowaniu, np. W testach.

Kiedy dostawca z nadrzędnego wtryskiwacza jest subskrybowany w komponencie podrzędnym, nie zostanie zniszczony po zniszczeniu komponentu, to jest obowiązkiem komponentu, aby anulować subskrypcję komponentu ngOnDestroy(jak wyjaśnia inna odpowiedź).

Estus Flask
źródło
Nie class Service implements OnDestroy? A jak myślisz, kiedy to się nazywa, jeśli usługa jest świadczona na poziomie modułu
Shumail,
1
implements OnDestroynie wpływa na nic, ale można je dodać w celu uzupełnienia. Zostanie wywołany, gdy moduł zostanie zniszczony, np appModule.destroy(). Może to być przydatne w przypadku wielu inicjalizacji aplikacji.
Estus Flask
1
czy wypisanie się z subskrypcji jest konieczne dla każdego komponentu korzystającego z usług?
Ali Abbaszade
2
Plunker nie działał dla mnie, więc oto wersja przykładu StackBlitz
compuguru
1
Miałem problemy ze zrozumieniem tego. Ale ta dyskusja pomogła mi zrozumieć różnicę między usługami lokalnymi i globalnymi: stackoverflow.com/questions/50056446/… Myślę, że to, czy musisz „posprzątać”, czy nie, zależy od zakresu twojej usługi.
Jasmin
26

Utwórz zmienną w swojej usłudze

subscriptions: Subscriptions[]=[];

Wypchnij każdą subskrypcję do tablicy jako

this.subscriptions.push(...)

Napisz dispose()metodę

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

Wywołaj tę metodę ze swojego komponentu podczas ngOnDestroy

ngOnDestroy(){
   this.service.dispose();
 }
Aravind
źródło
Dziękuję za odpowiedź. Czy mamy pojęcie, kiedy zostanie wywołane to ngOnDestroy. ?
mperle
tak, mówi, że to wywołanie czyszczenia, zanim dyrektywa lub komponent zostaną zniszczone. ale chcę tylko wiedzieć, czy ma to zastosowanie również do usług?
mperle
Żadne usługi nie zostaną wyczyszczone po wyładowaniu modułu
Aravind
2
haczyki cyklu życia nie mają zastosowania w przypadku@injectables
Aravind
@Aravind Nie jestem pewien, kiedy zostały wprowadzone, ale tak jest .
Estus Flask
11

Wolę ten takeUntil(onDestroy$)wzorzec udostępniany przez operatorów pipable. Podoba mi się, że ten wzorzec jest bardziej zwięzły, bardziej przejrzysty i jasno przekazuje zamiar zakończenia subskrypcji po wykonaniu instrukcjiOnDestroy haka cyklu życia.

Ten wzorzec działa w przypadku usług, a także składników subskrybujących wstrzyknięte obserwable. Poniższy kod szkieletowy powinien zawierać wystarczająco dużo szczegółów, aby zintegrować wzorzec z własną usługą. Wyobraź sobie, że importujesz usługę o nazwie 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();
  }

Temat kiedy / jak wypisać się z subskrypcji jest obszernie omówiony tutaj: Angular / RxJs Kiedy powinienem wypisać się z subskrypcji

Matthew Marichiba
źródło
5

Dla wyjaśnienia - nie musisz niszczyć, Observablesa jedynie dokonane dla nich subskrypcje.

Wygląda na to, że inni zauważyli, że możesz teraz również korzystać ngOnDestroyz usług. Link: https://angular.io/api/core/OnDestroy

małpa
źródło
1
Czy możesz rozwinąć więcej na ten temat
Aravind
2

Ostrożnie, jeśli używasz tokenów

Próbując uczynić moją aplikację tak modułową, jak to tylko możliwe, często używam tokenów dostawców do świadczenia usługi dla składnika. Wygląda na to, że te NIE otrzymująngOnDestroy metod o nazwie :-(

na przykład.

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

Z sekcją dostawcy w komponencie:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

Mój ShopPaymentPanelServiceNIE ma swojegongOnDestroy metody wywoływanej, gdy składnik jest usuwany. Po prostu odkryłem to na własnej skórze!

Obejściem problemu jest udostępnienie usługi w połączeniu z useExisting.

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Kiedy to zrobiłem, ngOnDisposezostał wezwany zgodnie z oczekiwaniami.

Nie jestem pewien, czy jest to błąd, czy nie, ale bardzo nieoczekiwany.

Simon_Weaver
źródło
Świetna wskazówka! Zastanawiałem się, dlaczego to nie działa w moim przypadku (używałem abstrakcyjnego interfejsu klasy jako tokenu do konkretnej implementacji).
Andrei Sinitson