Кутова 4+ ngOnDestroy () в експлуатації - знищувати спостережувані


103

У кутовому додатку ми маємо ngOnDestroy()гачок життєвого циклу для компонента / директиви, і ми використовуємо цей хук, щоб скасувати підписку на спостережувані.

Я хочу очистити / відмітити спостережувані, які створені в @injectable()службі. Я бачив деякі дописи, в яких говорилося, що ngOnDestroy()їх також можна використовувати в службі.

Але чи це хороша практика та єдиний спосіб це зробити, і коли це буде викликано? хтось, будь ласка, поясніть.

Відповіді:


119

Гак життєвого циклу OnDestroy доступний у постачальників. Відповідно до документів:

Гак життєвого циклу, який викликається при знищенні директиви, труби або служби.

Ось приклад :

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

Зверніть увагу, що у наведеному вище коді Serviceє екземпляр, який належить Fooкомпоненту, тому він може бути знищений при Fooзнищенні.

Для постачальників, які належать до кореневого інжектора, це відбуватиметься при знищенні програми, це корисно, щоб уникнути витоків пам’яті з кількома завантажувальними стріппами, тобто під час тестів.

Коли постачальник з батьківського інжектора підписаний на дочірній компонент, він не буде знищений при знищенні компонента, це відповідальність компонента - скасувати підписку на компонент ngOnDestroy(як пояснюється в іншій відповіді).


Ні class Service implements OnDestroy? І що ви думаєте, коли це викликається, якщо послуга надається на рівні модуля
Shumail

1
implements OnDestroyні на що не впливає, але може бути доданий для повноти. Він буде викликаний, коли модуль буде знищений, наприклад appModule.destroy(). Це може бути корисно для кількох ініціалізацій додатків.
Estus Flask

1
чи необхідна відмова від підписки на кожен компонент, який використовує послуги?
Алі Аббассаде

2
Plunker не працював для мене, тому ось версія прикладу StackBlitz
compuguru

1
У мене були проблеми, щоб зрозуміти це. Але ця дискусія допомогла мені зрозуміти різницю між місцевими та глобальними сервісами: stackoverflow.com/questions/50056446/... Чи потрібно вам "прибирати" чи ні, залежить від сфери вашої послуги, я думаю.
Жасмін

25

Створіть змінну у своїй службі

subscriptions: Subscriptions[]=[];

Натисніть кожного з ваших підписаних на масив як

this.subscriptions.push(...)

Напишіть dispose()метод

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

Викличте цей метод із вашого компонента під час ngOnDestroy

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

Дякуємо за відповідь. Чи маємо ми уявлення, коли буде викликано цей ngOnDestroy. ?
mperle

так, він каже, що це виклик очищення до того, як директива або компонент буде знищений. але я просто хочу зрозуміти, чи можна це застосовувати і для обслуговування?
mperle

Жодні служби не будуть очищені, коли модуль вивантажений
Aravind

2
гачки життєвого циклу не застосовуються для@injectables
Aravind

@Aravind Я не впевнений, коли вони були представлені, але вони є .
Estus Flask

11

Мені більше подобається цей takeUntil(onDestroy$)шаблон, який увімкнено піпабельними операторами. Мені подобається, що цей шаблон є більш стислим, більш чистим, і він чітко передає намір вбити підписку після виконання 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


1
Будь ласка, детальніше це розкажіть
Aravind

2

Обережно при використанні токенів

Намагаючись зробити свою програму максимально модульною, я часто використовую маркери постачальника для надання послуги компоненту. Здається, вони НЕ отримують своїх ngOnDestroyметодів під назвою :-(

напр.

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

З розділом провайдера в компоненті:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

My ShopPaymentPanelServiceНЕ ngOnDestroyвикликає метод, коли компонент утилізується. Я просто це знайшов на важкому шляху!

Обхідним шляхом є надання послуги спільно з useExisting.

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Коли я це зробив, мене ngOnDisposeвикликали, як очікували.

Не впевнений, це помилка чи ні, але дуже несподіване.


Чудовий натяк! Мені було цікаво, чому в моєму випадку це не працює (я використовував абстрактний інтерфейс класу як маркер для конкретної реалізації).
Андрій Синітсон,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.