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

104

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

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

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

mperle
источник

Ответы:

121

Хук жизненного цикла 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? И что вы думаете, когда это вызывается, если услуга предоставляется на уровне модуля
Шумаил
1
implements OnDestroyни на что не влияет, но может быть добавлен для полноты картины. Он будет вызываться при уничтожении модуля, например appModule.destroy(). Это может быть полезно для инициализации нескольких приложений.
Estus Flask
1
необходимо ли отказываться от подписки для каждого компонента, использующего службы?
Али Аббасзаде
2
Plunker не работал у меня, поэтому вот версия примера для StackBlitz
compuguru
1
Мне было трудно это понять. Но это обсуждение помогло мне понять разницу между локальными и глобальными службами: stackoverflow.com/questions/50056446/… Я думаю, нужно ли вам «очищать» или нет, зависит от объема вашей службы.
Жасмин
27

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

subscriptions: Subscriptions[]=[];

Вставьте каждую подписку в массив как

this.subscriptions.push(...)

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

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

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

ngOnDestroy(){
   this.service.dispose();
 }
Аравинд
источник
Спасибо за ваш ответ. Есть ли у нас идеи, когда будет вызван этот ngOnDestroy? ?
mperle
да, он говорит, что это вызов очистки до того, как директива или компонент будут уничтожены. но я просто хочу понять, применимо ли это и для обслуживания?
mperle
Никакие службы не будут очищены при выгрузке модуля
Аравинд
2
крючки жизненного цикла неприменимы@injectables
Аравинд
@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
Не могли бы вы подробнее рассказать об этом
Аравинд
2

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

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

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

Simon_Weaver
источник
Отличная подсказка! Мне было интересно, почему это не работает в моем случае (я использовал абстрактный класс-интерфейс в качестве токена для конкретной реализации).
Андрей Синицон