15

I've been struggling around injecting services into each other. The following blog Circular Dependency in constructors and Dependency Injection is kind of confusing where it says

One of the two objects is hiding another object C

I get the following error while injecting Service class into each other

Can't resolve all parameters for PayrollService: (SiteService, StorageService, SweetAlertService, ?)

//abstractmodal.service.ts
@Injectable()
 export abstract class AbstractModel {

   abstract collection = [];

   constructor(private siteService: SiteService, private storageService: StorageService,
                private sweetalertService: SweetAlertService) {}


   setCollectionEmpty() {
      this.collection = [];
    }
}
//account-payable.service.ts
@Injectable()
export class AccountPayableService extends AbstractModel {

   public collection = [];

   constructor(private sS: SiteService,private stS: StorageService, private sws: SweetAlertService,
            private accpPoService: PayablePurchaseOrderService, private attachmentService: AttachmentService,
            private injectorService: InjectorService) { 
         super(sS, stS, sws);
     }
}
//injector.service.ts
@Injectable()
export class InjectorService {
   constructor(private payrollService: PayrollService) {}

   cleanPayrollCollection() {
     this.payrollService.setCollectionEmpty();
   }
}
//payroll.service.ts
@Injectable()
export class PayrollService extends AbstractModel {

   public collection = [];

   constructor(private sS: SiteService,private stS: StorageService, private sws: SweetAlertService,
            private accpService: AccountPayableService) { 
    super(sS, stS, sws);
   }
}

Your comments and answered will be appreciated a lot.

Thanks

Basheer Kharoti
  • 4,202
  • 5
  • 24
  • 50

3 Answers3

32

You can workaround circular dependencies by injecting Injector instead of one of the services that cause the circular dependency

private payrollService:PayrollService;
constructor(/*private payrollService:PayrollService*/ injector:Injector) {
  setTimeout(() => this.payrollService = injector.get(PayrollService));
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    `Maximum call stack size exceeded` – Basheer Kharoti Nov 10 '16 at 11:09
  • I've deleted `InjectorService` and just tried injecting both services through `Injector` – Basheer Kharoti Nov 10 '16 at 11:10
  • And this worked? `setTimout()` might also be required to break the cycle (don't remember). – Günter Zöchbauer Nov 10 '16 at 11:12
  • that didn't worked and throws `inline template:0:0 caused by: Maximum call stack size exceeded` error – Basheer Kharoti Nov 10 '16 at 11:13
  • Sorry, then I don't know. – Günter Zöchbauer Nov 10 '16 at 11:13
  • 2
    Where do I import the `Injector` class from ? Is it `@angular/core` ? I can't seem to find the info online – Mouradif Mar 07 '17 at 11:10
  • 1
    @KiJéy, I couldn't find it either, but when I tried `@angular/core` it worked. – ShadSterling Mar 08 '17 at 14:52
  • I got the same error when I used the injector in either constructor or load; if I do it in a method invoked later it fixes the problem. – ShadSterling Mar 08 '17 at 14:54
  • 4
    As @Polyergic says, manually injecting in the constructor is not too useful. I found that methods were getting called before setTimeout completed. I moved the manual injection to just before the dependency's usage. Also, removed the setTimeout. – Richard Matsen Apr 06 '17 at 04:40
  • forwardRef is used in cases where circular reference cannot be avoided – ZooZ Jul 23 '17 at 07:15
  • @ZooZ `forwardRef` doesn't help for this usecase. `forwardRef` is used when you have for example 2 classes within one file where each one refers the other (directly or transitively) which would cause a static error. With DI such a circular depndency akso breaks at runtime also when the classes are in different files and `forwardRef` doesn't help here. – Günter Zöchbauer Jul 23 '17 at 10:59
  • 2
    sorry to dig the topic, but circular references can always be avoided. That's exactly what's meant in "One of the two objects is hiding another object C". If you think it can't be avoided, it just means that one of them should be divided in 2 distinct ones. In angularJs at least (never tried in angular2), it's also possible to "add" functions to a service you injected, so that many cases where you would need circular can be solved like this. – Kaddath Apr 20 '18 at 16:35
  • @Kaddath you are right in general. I have seen a few situations where it might be a bit difficult. For example injecting `ApplicationRef` to `ErrorHandler` (not sure if I remember correctly). But usually there should be a better way like you mentioned. – Günter Zöchbauer Apr 20 '18 at 16:59
  • For me adding the private object at the top required me to add the Import that cause the circular dependency. Any way around this? – Parrotmaster Aug 15 '19 at 08:56
1

In my case (Angular 4 in an Ionic project) even injecting the service just before it's usage (upon user interaction) was not working (Same "Can't resolve all parameters..." at startup).

What worked for me was to inject (and thus provide) the service by name.

So in my app module :

...
providers: [{provide: "MyServiceName", MyServiceClass}],
...

And when needed :

const myService: MyServiceClass = injector.get("MyServiceName");
...
Jul
  • 31
  • 5
0

I know the post is old but thanks anyways above solution has worked for me as i had cyclic dependency problem so i have to use injector to avoid creating mediator service to communicate, Although one correction in above solution

From Angular 5 onwards we need to use:

providers: [{provide: "MyServiceName",useclass: MyServiceClass}],

and then

injector.get('MyServiceName')

although get is derpecated

Nikhil Kamani
  • 850
  • 9
  • 12
  • You should be more specific, which file with which imports, it would have been helpful for me @least – Pipo Jan 12 '20 at 13:53