0

I am using Ionic 2 rc4.

I have an issue where I am getting an error due to a cyclical dependency.

  +-------------------------------------+
  |    LoginEmailPage    RegisterPage   |
  |           ↓           ↓↑            |
  |           UtilityService            |
  +-------------------------------------+

I have a class LoginEmailPage that makes use of UtilityService.

e.g. from LoginEmailPage:

this.utilityService.login(nav)

In UtilityService:

public login(nav: NavController): void {
    nav.popTo(RegisterPage);
}

RegisterPage also makes use of the utilityService, so I get an compile time error:

metadata_resolver.js:623Uncaught Error: Can't resolve all parameters for LoginEmailPage: (FirebaseAuth, MenuController,

NavController, NavParams, FormBuilder, ViewController, AlertController, PersonService, ?, Events, LoadingController). at CompileMetadataResolver._getDependenciesMetadata (http://localhost:8100/build/main.js:39298:19)

Where the ? is UtilityService.

Question

How can I have a service (UtilityService) navigate to a page (RegisterPage) that already references the service(UtilityService)?

Thanks

UPDATE

Due to advise below:

Based on this, I have tried using an Injector:

utilityService.ts

import { Injectable, Injector } from "@angular/core";

    private registerPage: RegisterPage = null;
    constructor(injector: Injector,...
        setTimeout(() => this.registerPage = injector.get(RegisterPage));

    public login(nav: NavController): void {
        nav.popTo(this.registerPage);
    }

But I still get the same error as above.

Second, I look at this and this, which suggests to make use of forwardRef. My problem is that my service is an @Injectable and not a @Component. So the examples I see, does something like:

@Component({
  selector: 'my-button',
  template: `<div>
               <icon></icon>
               <input type="button" />
             </div>`,
  directives: [forwardRef(() => MyIcon)] // MyIcon has not been defined yet
})                                       // forwardRef resolves as MyIcon when MyIcon is needed

Question

How can I get this to work with a service that is not a component, but rather an injectable?

UPDATE

Fore more information, I am adding the constructors of each file.

loginemail.ts

@Component({
    templateUrl: 'loginemail.html'
})

export class LoginEmailPage {
...
    constructor(public auth: FirebaseAuth, menu: MenuController, public nav: NavController, public navParams: NavParams, public builder: FormBuilder, public viewCtrl: ViewController, alertCtrl: AlertController, personService: PersonService, utilityService: UtilityService, events: Events, public loadingCtrl: LoadingController) {

register.ts

@Component({
    templateUrl: 'register.html'
})

export class RegisterPage {
...
    constructor(public nav: NavController, public navParams: NavParams, public builder: FormBuilder, personService: PersonService,
        public viewCtrl: ViewController, public alertCtrl: AlertController, utilityService: UtilityService, events: Events,
        public loadingCtrl: LoadingController, public auth: FirebaseAuth) {

utilityService.ts

@Injectable()
export class UtilityService {
...
    constructor(injector: Injector, alertCtrl: AlertController, menu: MenuController, popoverController: PopoverController, storage: Storage) {

UPDATE

Thanks to advise below, I try this.

utilityService.ts

import { Injectable, Inject, forwardRef } from "@angular/core";
import { RegisterPage } from '../register/register';

@Injectable()
export class UtilityService {
...

public registerPage: RegisterPage = null;

     constructor(@Inject(forwardRef(() => RegisterPage)) registerPage, ...
        this.registerPage = registerPage;
          ...
          nav.popTo(this.registerPage, { personModel });

But I still the original compile error. I must be doing something wrong. Any ideas?

UPDATE

In the RegisterPage, I use forwardRef:

constructor(@Inject(forwardRef(() => UtilityService)) utilityService

But I get the following error:

Error: No provider for RegisterPage!

So I add RegisterPage to the providers in app.modula.ts, but then I get:

Error: Uncaught (in promise): Error: Provider parse errors:
Cannot instantiate cyclic dependency! RegisterPage: in NgModule AppModule
Error: Provider parse errors:
Cannot instantiate cyclic dependency! RegisterPage: in NgModule AppModule
Community
  • 1
  • 1
Richard
  • 8,193
  • 28
  • 107
  • 228
  • Sounds like http://stackoverflow.com/questions/40860202/di-with-cyclic-dependency-with-custom-http-and-configservice/40860233#40860233 – Günter Zöchbauer Feb 01 '17 at 08:52
  • you could use forward reference https://angular.io/docs/ts/latest/cookbook/dependency-injection.html#!#forwardref – Suraj Rao Feb 01 '17 at 08:52
  • Thanks for you assistance, but I have tried an `Injector` and a `forwardRef` as you guys suggest, but cannot get either to work. I have UPDATED my explanation above to include more detail on this. If you can suggest anything, I would appreciate it. – Richard Feb 01 '17 at 09:18
  • `forwardRef` should not be necessary if each class is in its own file. Only if you need to reference classes further down in the same file `forwardRef` is required. – Günter Zöchbauer Feb 01 '17 at 09:19
  • I guess we need to see the constructors with parameters list of all involved service and component classes. – Günter Zöchbauer Feb 01 '17 at 09:21
  • Yes, each class is in its own file. Will add the constructors... – Richard Feb 01 '17 at 09:21
  • I don't get from your graphic at the top what exactly the cycle looks like. Perhaps you need to inject the `Injector` somewhere else. `forwardRef` won't help you here. The problem is that Angular can't inject a class before the constructor is called, and it can't pass constructor parameters after the class was instantiated. `forwardRef` is only to break cycles in TypeScript code, not in constructor parameter dependencies. – Günter Zöchbauer Feb 01 '17 at 11:18

1 Answers1

1

Try using forwardRef in the constructor of your component eg:

export class LoginEmailPage {
constructor(...
     @Inject(forwardRef(() => UtilityService))utilityService, events: Events,
        public loadingCtrl: LoadingController, public auth: FirebaseAuth) {

Check here

Also RegisterPage and LoginEmailPage are components not providers (injectable). It should not be set in constructor of UtilityService.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
  • Hi Suraj, thanks for your input. I have tried what you suggest, but I must be doing something incorrectly. Please can you have a look at my UPDATE in the description above. – Richard Feb 01 '17 at 10:22
  • or try in login page since there you are getting error. I do not know your dependency graph to know wer the cycle is – Suraj Rao Feb 01 '17 at 10:25
  • I can make reference to the UtilityService from the `RegisterPage` and `LoginEmailPage`, so I need to do this in the `UtilityPage`. I will update the description above to show my dependencies more clearly. – Richard Feb 01 '17 at 10:34
  • Are you importing Register page in loginemailpage? – Suraj Rao Feb 01 '17 at 10:50
  • I don't thin that `forwardRef` will work, because I think the `import` of the cyclic dependency is causing the error. I did look at using `require`, but couldn't get that to work either. (e.g. `this.pages['RegisterPage'] = require('../register/register').RegisterPage;`) – Richard Feb 01 '17 at 10:51
  • Yes, I am importing the `RegisterPage` in the `LoginEmailPage`. But not visa versa. – Richard Feb 01 '17 at 10:52
  • then you need to to use forwardRef utilityservice in loginemailpage – Suraj Rao Feb 01 '17 at 10:53
  • Thanks, will give it a try. – Richard Feb 01 '17 at 10:54
  • I try add the forwardRef to the `LoginEmailPage` and `RegisterPage` for the `UtilityService`, but I get: `Error: No provider for RegisterPage!`. See UPDATE above. – Richard Feb 01 '17 at 11:14
  • The page is a component not a provider.. remove from constructor..You can use in the service like you use in any component (eg. pushing in navcontroller) – Suraj Rao Feb 01 '17 at 11:16
  • I'm confused, you say I must remove it from the constructor, but then I don't have a forwardRef and I'm back to my original code. – Richard Feb 01 '17 at 12:56
  • forward ref the service(provider) in component. component cant be injected in providers constructor – Suraj Rao Feb 01 '17 at 12:58
  • That's my whole question from the beginning. I have a cyclic dependency because my service needs to popTo the component. So I either need to reference the component from the service, or a total redesign. – Richard Feb 01 '17 at 13:07
  • In the loginemailpage you refer RegisterPage by only importing and not by injecting in constructor.. thats my point. you do the same way in provider – Suraj Rao Feb 01 '17 at 13:08
  • import and use in provider and dont inject – Suraj Rao Feb 01 '17 at 13:09
  • I see, thanks for your patience. From the `UtilityService`, when I `import` the `RegisterPage`, I get no errors, but as soon as I `nav.popTo(RegisterPage)`, I get original compilation error. – Richard Feb 01 '17 at 13:20
  • Yes, I still get the original error posted. `Can't resolve all parameters for LoginEmailPage: (?, FirebaseAuth...`. It's as if it doesn't like the `LoginEmailPage` using a service that makes reference to a page it already imports. – Richard Feb 01 '17 at 13:26
  • then am not sure.. forwardref on provider has worked for me in a similar situation – Suraj Rao Feb 01 '17 at 13:28
  • Thank you for your help. I will continue to try with forwardRef. I think you have pointed me in the right direction. – Richard Feb 01 '17 at 13:43