21

I'm trying to implement a ConfigService to retrieve the right configuration for the right environment in the project. I'm currently encountering a cyclic dependancy

(index):28 Error: (SystemJS) Provider parse errors:
    Cannot instantiate cyclic dependency! Http: in NgModule AppModule
    Error: Provider parse errors:

I've explored the code and there is the problem, in my opinion:

CustomHttp

constructor(backend: XHRBackend, options: RequestOptions, public spinnerService: SpinnerService, public exceptionService: ExceptionService, public configService: ConfigService) 

ExceptionService

constructor(private _notificationService: NotificationService, private _spinnerService: SpinnerService, private _configService: ConfigService, private _router: Router)

ConfigService

constructor(private http: Http) {}

As you can see, I've a cyclic dependancies illustrated in this diagram (without any good convention):

enter image description here

My question now is, how to fix it? I've heard of Injector but I'm not sure I can really use it in this context.

Thanks in advance for your answer.

LoïcR
  • 4,940
  • 1
  • 34
  • 50

1 Answers1

29

DI can't resolve cyclic dependencies. A workaround is to inject the injector and acquire the instance imperatively:

@Injectable()
class ConfigService {
  private http: Http;
  constructor(injector:Injector) {
    setTimeout(() => this.http = injector.get(Http);
  }
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Is there in the meantime another approach with Angular 4? – CSchulz May 23 '17 at 11:46
  • That is for Angular 2 - 4 – Günter Zöchbauer May 23 '17 at 12:00
  • I hoped in the meantime there is an improvement for this. – CSchulz May 23 '17 at 12:26
  • There are no plans to change that. This would required to change how constructors work in TypeScript in a way nobody would want them to be changes. Don't expect a change here. – Günter Zöchbauer May 23 '17 at 12:27
  • I just tested without, and then with the setTimeout. And it works only with the latter. Any hint of why the delay is required? – wiwi Jul 13 '17 at 11:41
  • 5
    Because `new ConfigService()` needs to return, and only afterwards can `injector.get(Http)` create a `Http` instance that depends on `ConfigService`. *In the midst* of the constructor of `ConfigService` it's not possible for `injector.get(Http)` get an instance. `setTimeout()` allows to complete the constructor, and only later requests the `Http` class. This `setTimeout` is what actually breaks the cycle, the other stuff is just boilerplate for that. – Günter Zöchbauer Jul 13 '17 at 11:45
  • There is now a more direct support for this kind of dependency: [ForwardRef](https://angular.io/api/core/forwardRef). – Coderer Dec 10 '18 at 14:48
  • `forwardRef` doesn't help for that. `forwardRef` is only for static analysis of TS code, not for breaking up circular dependencies in DI. – Günter Zöchbauer Dec 10 '18 at 15:22