echo_salik
echo_salik

Reputation: 859

Solving cyclic dependency due to APP_INITIALIZER

I have a service AuthenticationService that loads AngularFirestore. The service is loaded in RootComponent. All app modules are lazy-loaded in RootComponent (it has the main router-outlet). Now there are many sub-modules that also load AngularFirestore.

I have to ensure that AuthenticationService is initialized (async stuff) before the components and modules are loaded up, so I have put it in APP_INITIALIZER provider. This causes cyclic-dependency Cannot instantiate cyclic dependency! AngularFirestore.

If I don't put it in APP_INITIALIZER it works but the app runs without AuthenticationService being initialized.

Is there a way around this? (besides adding auth.initialize() in all components)

App tree:

AppComponent -> RootComponent(AuthenticationService) -> All Submodules(AuthenticationService, AngularFirestore)
// constructor of AuthenticationService
constructor(
    private auth : AngularFireAuth,
    private remoteconfig : AngularFireRemoteConfig,
    private keepalive: Keepalive,
    private idle: Idle, 
    private toast : ToastService,
    private router : Router,
    private fs : AngularFirestore,
    private http : HttpClient,
)
// providers in app.module.ts
{
  provide: APP_INITIALIZER,
  multi: true,
  useFactory: authFactory,
  deps: [
    AuthenticationService
  ]
}
// Factory for APP_INITIALIZER
export function authFactory(
  auth : AuthenticationService
) {
  return () : Promise<any> => auth.initialize();
}

Regards!

Upvotes: 2

Views: 494

Answers (1)

echo_salik
echo_salik

Reputation: 859

So after going through a plethora of github comments and unrelated SO questions I came up with a solution that I skimmed through for an unrelated issue in Github comments.

TLDR: Make injection by creating a new service for AngularFirestore just for AuthenticationService or for the whole app. I went with the latter.

Understanding the problem:

From what I understand, the issue is because my AuthenticationService loads AngularFirestore and makes it part of its dependency tree. Now any subsequent injection of AuthenticationService and AngularFirestore makes a cyclic dependency, because injecting AuthenticationService makes AngularFirestore part of the dependency tree, and when I inject AngularFirestore after that it creates two injections of AngularFirestore. Hence the error. I may be completely wrong, but I think this was the issue.

Solution:

Create a service for AngularFirestore import. This, I think, moves it out of the dependency tree and injects it as a service making it OK for any subsequent injections of AngularFirestore safe.

// appfirestoreservice.ts
import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';

@Injectable({
  providedIn: 'root'
})
export class AppFirestoreService {
  
  public fs : AngularFirestore;
  public readonly collection = this._fs.collection;
  public readonly doc = this._fs.doc;
  public readonly createId = this._fs.createId;

  constructor(
    private _fs : AngularFirestore,
  ) {
    this.fs = _fs;
  }
}
// App Initializer in AppModule
{
  provide: APP_INITIALIZER,
  multi: true,
  useFactory: initFactory,
  deps: [
    AppServiceInitializerService,
    RemoteConfigService,
    AuthenticationService,
  ]
},

For my case I had to create a service just to load RemoteConfigService and AuthenticationService because they have to be initialized one after the other.

Regards!

Upvotes: 1

Related Questions