Angular 4+ ngOnDestroy() в обслуживании - уничтожить наблюдаемые

В приложении angular есть крючок жизненного цикла ngOnDestroy() для компонента/директивы, и мы используем этот крючок для отмены подписки на наблюдаемые.

Я хочу очистить/уничтожить наблюдаемые, созданные в службе @injectable(). Я видел несколько сообщений о том, что ngOnDestroy() можно использовать и в сервисе.

Но, это хорошая практика и единственный способ сделать это, и когда он будет вызван? кто-то прояснит.

Ответ 1

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

Обратите внимание, что в приведенном выше коде Service - это экземпляр, относящийся к компоненту Foo, поэтому он может быть уничтожен при уничтожении Foo.

Для поставщиков, которые относятся к корневому инжектору, это произойдет при уничтожении приложения, это поможет избежать утечек памяти с помощью нескольких бутстрапов, т.е. в тестах.

Когда поставщик из родительского инжектора подписывается в дочернем компоненте, он не будет уничтожен при уничтожении компонента, это ответственность компонента за отмену подписки в компоненте ngOnDestroy (как объясняет другой ответ).

Ответ 2

Создайте переменную в своей службе

subscriptions: Subscriptions[]=[];

Нажимайте каждую из своих подписчиков на массив как

this.subscriptions.push(...)

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

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

Вызовите этот метод из своего компонента во время ngOnDestroy

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

Ответ 3

Просто чтобы уточнить - вам не нужно уничтожать Observables, а только подписки на них.

Похоже, что другие указали, что теперь вы можете использовать ngOnDestroy также со службами. Ссылка: https://angular.io/api/core/OnDestroy

Ответ 4

Я предпочитаю этот паттерн 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

Осторожно при использовании токенов

Пытаясь сделать свое приложение максимально модульным, я часто использую токены провайдера для предоставления услуги компоненту. Похоже, что эти методы 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 был вызван, как и ожидалось.

Не уверен, если это ошибка или нет, но очень неожиданно.