web-dev-qa-db-ja.com

Angular 4+ ngOnDestroy()サービス中-オブザーバブルを破棄

angularアプリケーションには、コンポーネント/ディレクティブ用のngOnDestroy()ライフサイクルフックがあり、このフックを使用してオブザーバブルのサブスクライブを解除します。

@injectable()サービスで作成されたobservableをクリア/破壊したい。 ngOnDestroy()はサービスでも使用できると言っている投稿を見ました。

しかし、それは良い習慣であり、そうする唯一の方法であり、いつ呼び出されますか?誰か明確にしてください。

59
mperle

OnDestroy ライフサイクルフックはプロバイダーで使用できます。ドキュメントによると:

ディレクティブ、パイプ、またはサービスが破棄されたときに呼び出されるライフサイクルフック。

です:

@Injectable()
class Service implements OnDestroy {
  ngOnDestroy() {
    console.log('Service destroy')
  }
}

@Component({
  selector: 'foo',
  template: `foo`,
  providers: [Service]
})
export class Foo {
  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)
  }
}

上記のコードでは、ServiceFooコンポーネントに属するインスタンスであるため、Fooが破棄されると破棄されることに注意してください。

ルートインジェクターに属するプロバイダーの場合、これはアプリケーションの破棄時に発生します。これは、複数のブートストラップ、つまりテストでのメモリリークを回避するのに役立ちます。

親インジェクターからのプロバイダーが子コンポーネントでサブスクライブされると、コンポーネントの破棄で破棄されません。これは、コンポーネントngOnDestroyでサブスクライブを解除するコンポーネントの責任です(別の答えで説明します)。

72
Estus Flask

サービスに変数を作成します

subscriptions: Subscriptions[]=[];

各サブスクライブを配列にプッシュします

this.subscriptions.Push(...)

dispose()メソッドを書く

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

NgOnDestroy中にコンポーネントからこのメソッドを呼び出します

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

このtakeUntil(onDestroy$)パターンは、pipable演算子によって有効にされるのが好きです。このパターンは、より簡潔で、よりクリーンであり、OnDestroyライフサイクルフックの実行時にサブスクリプションを強制終了する意図を明確に伝えることが好きです。

このパターンは、注入されたオブザーバブルをサブスクライブするコンポーネントと同様にサービスに対しても機能します。以下のスケルトンコードは、パターンを独自のサービスに統合するのに十分な詳細を提供します。 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();
  }

いつ/どのように購読を解除するかのトピックは、ここで広範囲にカバーされます: Angular/RxJsいつ「購読」から購読を解除すべきですか

5

明確にするために-Observablesを破棄する必要はありませんが、サブスクリプションのみを破棄する必要があります。

他の人が、あなたがサービスでngOnDestroyを使用できるようになったと指摘しているようです。リンク: https://angular.io/api/core/OnDestroy

5
apeshev

トークンを使用する場合の注意

アプリケーションを可能な限りモジュール化するために、プロバイダートークンを使用してコンポーネントにサービスを提供することがよくあります。これらはngOnDestroyというメソッドを取得していないようです:-(

例えば。

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

コンポーネントにプロバイダーセクションがある場合:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

私のShopPaymentPanelServiceには、コンポーネントが破棄されるときにngOnDestroyメソッドが呼び出されません。私はこれを難しい方法で見つけました!

回避策は、useExistingとともにサービスを提供することです。

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

これを行ったとき、ngOnDisposeが期待どおりに呼び出されました。

これがバグかどうかはわかりませんが、非常に予想外です。

0
Simon_Weaver