3

HelloComponent gets a SampleService instance, defining a Service Provider. When HelloCompoment is destroyed, I don't understand why SampleService survives.

If HelloComponent got a SampleService instance by type (avoiding ServiceProvider), no problem occurs.


sample-service.ts

@Injectable()
export class SampleService implements OnDestroy{

 constructor(){
   console.log('created new sample service');
 }

 ngOnDestroy(){
  console.log('destroyed sample service');
 }
}

hello-component.ts

import { Component, OnInit, OnDestroy } from '@angular/core'
import { SampleService } from '../service/sample.service'

let ServiceFactory = () => {
  console.log('Providing new SampleService');
  return new SampleService();
};

let ServiceProvider = { 
    provide: SampleService,
    useFactory: ServiceFactory
  };

@Component({
  selector: 'hello',
  templateUrl: './hello.component.html',
  providers: [ServiceProvider]
})
export class HelloComponent implements OnInit, OnDestroy {

constructor(private sampleService: SampleService){}

ngOnInit(){
  console.log("Hello component created!")
}

ngOnDestroy(){
  console.log("Hello component destroyed!")
 }
}

Here stackblitz: https://stackblitz.com/edit/angular-vkhmma (click on toggleHello and see console logs)

How could I force the service destroying when component ends?

Fabio Formosa
  • 904
  • 8
  • 27

2 Answers2

5

This is a known issue in Angular, but one that is (unfortunately) by design.

The presence of an OnDestroy callback hook is checked at compile time and since your ServiceProvider is wrapping a Factory which creates a SampleService, the Angular compiler unfortunately has no idea that this hook even exists, so it will never be called.

Darren Ruane
  • 2,385
  • 1
  • 8
  • 17
  • Ok, all reported issues have been closed a year ago. I'm a little bit confused, I'm using latest stable ver (7.2.x). BTW, my service subscribes to some $subjects (rxjs) and it starts polling that never stops. any workaround? – Fabio Formosa Apr 08 '19 at 17:51
  • Yes because it is something that Angular have decided is **by design**. I presume they have no intentions of addressing it. I am not certain if there's any way around this I'm afraid, other than providing your service directly. You could also have your service only return an observable but **not** subscribe to it and then have the calling classes subscribe to that. – Darren Ruane Apr 08 '19 at 18:24
0

You can inject a service with OnDestroy to the service made by factory:

{
  provide: ServiceWithoutOnDestroy,
  useFactory: (..., unsubscribingService: UnsubscribingService) => new ServiceWithoutOnDestroy(..., unsubscribingService),
  deps: [..., UnsubscribingService],
}

unsubscribing.service.ts

/**
 * ## Usage
 *
 * <pre>
 *   @Component({
 *     ...,
 *     providers: [UnsubscribingService],
 *   })
 *
 *   ...
 *
 *   private destroyed$ = this.unsubscribingService.destroyed$;
 *
 *   ...
 *
 *   constructor(
 *     ...,
 *     private unsubscribingService: UnsubscribingService
 *   )
 *
 *   ...
 *
 *   source$.pipe(
 *     ...,
 *     takeUntil(this.destroyed$)
 *   )
 *
 *   ...
 * </pre>
 */
@Injectable()
export class UnsubscribingService implements OnDestroy {
  readonly destroyed$ = new Subject<void>();

  ngOnDestroy() {
    this.destroyed$.next();
    this.destroyed$.complete();
  }
}
Yaroslav
  • 118
  • 1
  • 8