After running into this issue a few times, I've devised a good way to overcome it by using a getter with the Angular Injector
service, instead directly injecting the service in the constructor. This allows the service time to be constructed before being referenced. My example uses only services but the same thing can be applied to a component using a service, just put the getter in a component instead BService
in the example.
What I did was use a getter to inject the service into a class property using the Injector
class, if the class property was not already set before, so the service is only ever injected once (the first time the getter is called). This allows the service to be used in basically the same way as if it was injected in the constructor but without a circular reference error. Just use the getter this.aService
. They only time this won't work is if you are trying to use AService
within the constructor of Bservice
, then you would have the same issue of a circular reference since Aservice
would not be ready yet. By using the getter you are deferring injecting the service until you need it.
There are arguments that, AService
depending on BService
, and BService
depending on AService
, is bad form but there exceptions to every rule and every situation is different so this is an easy and effective way to deal with this issue in my opinion.
// a.service.ts
import { Injectable } from '@angular/core';
import { BService } from './b.service';
@Injectable({
providedIn: 'root'
})
export class AService {
constructor(
private bService: BService,
) { }
public foo() {
console.log('foo function in AService!');
this.bService.bar();
}
}
// b.service.ts
import { Injectable, Injector } from '@angular/core';
import { AService } from './a.service';
@Injectable({
providedIn: 'root'
})
export class BService {
// Use the getter 'aService' to use 'AService', not this variable.
private _aService: AService;
constructor(
private _injector: Injector,
) { }
// Use this getter to use 'AService' NOT the _aService variable.
get aService(): AService {
if (!this._aService) {
this._aService = this._injector.get(AService);
}
return this._aService;
}
public bar() {
console.log('bar function in BService!');
this.aService.foo();
}
}