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
был вызван, как и ожидалось.
Не уверен, если это ошибка или нет, но очень неожиданно.