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.

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ź).


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();
 }

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


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


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.


Ś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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.