Circular dependency injection angular 2
Asked Answered
B

3

16

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

Bluegreen answered 10/11, 2016 at 10:55 Comment(0)
C
33

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));
}
Careful answered 10/11, 2016 at 11:1 Comment(14)
Maximum call stack size exceededBluegreen
I've deleted InjectorService and just tried injecting both services through InjectorBluegreen
And this worked? setTimout() might also be required to break the cycle (don't remember).Dorita
that didn't worked and throws inline template:0:0 caused by: Maximum call stack size exceeded errorBluegreen
Sorry, then I don't know.Dorita
Where do I import the Injector class from ? Is it @angular/core ? I can't seem to find the info onlineKeek
@KiJéy, I couldn't find it either, but when I tried @angular/core it worked.Manual
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.Manual
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.Engedi
forwardRef is used in cases where circular reference cannot be avoidedLexy
@Lexy 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.Dorita
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.Aport
@Aport 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.Dorita
For me adding the private object at the top required me to add the Import that cause the circular dependency. Any way around this?Greenburg
S
2

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");
...
Speedway answered 26/3, 2018 at 14:37 Comment(0)
D
1

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

Diglot answered 25/8, 2018 at 5:1 Comment(1)
You should be more specific, which file with which imports, it would have been helpful for me @leastOrlena

© 2022 - 2024 — McMap. All rights reserved.