Angular 4+ ngOnDestroy () dalam layanan - hancurkan yang dapat diamati

104

Dalam aplikasi bersudut kita memiliki ngOnDestroy()hook siklus hidup untuk sebuah komponen / direktif dan kita menggunakan hook ini untuk berhenti berlangganan observable.

Saya ingin menghapus / menghancurkan observasi yang dibuat dalam sebuah @injectable()layanan. Saya melihat beberapa posting yang mengatakan bahwa ngOnDestroy()dapat digunakan dalam layanan juga.

Tapi, apakah ini praktik yang baik dan satu-satunya cara untuk melakukannya dan Kapan itu akan dipanggil? seseorang tolong klarifikasi.

mperle
sumber

Jawaban:

121

Pengait siklus hidup OnDestroy tersedia di penyedia. Menurut dokumen:

Pengait siklus hidup yang dipanggil saat perintah, pipa, atau layanan dimusnahkan.

Berikut contohnya :

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

Perhatikan bahwa kode di atas Serviceadalah sebuah instance milik Fookomponen, sehingga dapat dimusnahkan jika Foodimusnahkan.

Untuk penyedia yang termasuk dalam root injector, hal ini akan terjadi pada penghancuran aplikasi, hal ini berguna untuk menghindari kebocoran memori dengan beberapa bootstrap, misalnya dalam pengujian.

Ketika penyedia dari injektor induk berlangganan komponen anak, itu tidak akan dimusnahkan saat komponen dihancurkan, ini adalah tanggung jawab komponen untuk berhenti berlangganan dalam komponen ngOnDestroy(seperti jawaban lain menjelaskan).

Estus Flask
sumber
Tidak class Service implements OnDestroy? Dan apa yang Anda pikirkan saat ini dipanggil jika layanan disediakan pada tingkat modul
Shumail
1
implements OnDestroytidak mempengaruhi apa pun tetapi dapat ditambahkan untuk kelengkapan. Ini akan dipanggil ketika modul dihancurkan, seperti appModule.destroy(). Ini mungkin berguna untuk beberapa inisialisasi aplikasi.
Estus Flask
1
apakah berhenti berlangganan diperlukan untuk setiap komponen yang menggunakan layanan?
Ali Abbaszade
2
Plunker tidak berfungsi untuk saya, jadi inilah contoh versi StackBlitz
compuguru
1
Saya mengalami kesulitan untuk memahami ini. Tetapi diskusi ini membantu saya memahami perbedaan antara layanan lokal dan global: stackoverflow.com/questions/50056446/… Apakah Anda harus "membersihkan" atau tidak tergantung dari cakupan layanan Anda, menurut saya.
Jasmin
27

Buat variabel dalam layanan Anda

subscriptions: Subscriptions[]=[];

Dorong setiap langganan Anda ke array sebagai

this.subscriptions.push(...)

Tulis dispose()metode

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

Panggil metode ini dari komponen Anda selama ngOnDestroy

ngOnDestroy(){
   this.service.dispose();
 }
Aravind
sumber
Terimakasih atas balasan anda. Apakah kita tahu kapan ngOnDestroy ini akan dipanggil. ?
mperle
ya itu mengatakan itu panggilan pembersihan sebelum direktif atau komponen dihancurkan. tetapi saya hanya ingin memahami apakah itu berlaku untuk layanan juga?
mperle
Tidak ada layanan yang akan dihapus ketika modul dibongkar
Aravind
2
kait siklus hidup tidak berlaku untuk@injectables
Aravind
@Aravind Saya tidak yakin kapan mereka diperkenalkan tetapi mereka diperkenalkan .
Estus Flask
11

Saya lebih suka takeUntil(onDestroy$)pola ini diaktifkan oleh operator pipable. Saya suka bahwa pola ini lebih ringkas, lebih bersih, dan dengan jelas menyampaikan maksud untuk menghentikan langganan setelah eksekusi OnDestroypengait siklus proses.

Pola ini berfungsi untuk layanan serta komponen yang berlangganan observable yang diinjeksi. Kode kerangka di bawah ini akan memberi Anda cukup detail untuk mengintegrasikan pola ke dalam layanan Anda sendiri. Bayangkan Anda sedang mengimpor layanan bernamaInjectedService ...

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

Topik kapan / bagaimana berhenti berlangganan dibahas secara luas di sini: Angular / RxJs Kapan saya harus berhenti berlangganan dari `Subscription`

Matthew Marichiba
sumber
5

Hanya untuk memperjelas - Anda tidak perlu menghancurkan Observables tetapi hanya langganan yang dibuat untuk mereka.

Sepertinya orang lain telah menunjukkan bahwa Anda sekarang dapat menggunakan ngOnDestroylayanan juga. Tautan: https://angular.io/api/core/OnDestroy

apeshev
sumber
1
Bisakah Anda menjelaskan lebih lanjut tentang itu
Aravind
2

Hati-hati jika menggunakan token

Dalam mencoba membuat aplikasi saya sesederhana mungkin, saya akan sering menggunakan token penyedia untuk menyediakan layanan ke sebuah komponen. Tampaknya ini TIDAK mendapatkan merekangOnDestroy metode disebut :-(

misalnya.

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

Dengan bagian penyedia dalam sebuah komponen:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

Saya ShopPaymentPanelServiceTIDAK memilikinyangOnDestroy metode yang dipanggil saat komponen dibuang. Saya baru saja menemukan ini dengan cara yang sulit!

Solusinya adalah menyediakan layanan terkait dengan useExisting.

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Ketika saya melakukan ini, ngOnDisposedipanggil seperti yang diharapkan.

Tidak yakin apakah ini bug atau bukan tapi sangat tidak terduga.

Simon_Weaver
sumber
Petunjuk bagus! Saya bertanya-tanya mengapa itu tidak berfungsi dalam kasus saya (saya menggunakan antarmuka kelas abstrak sebagai token untuk implementasi konkret).
Andrei Sinitson