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.