1

I am using the APP_INITIALIZER token to do something on page load before my Angular app is bootstrapped. The service I am using for that logic depends on another service I have in my CoreModule.

I know this error is happening because of the fact that I'm injecting the AuthService into my AppService, however I can't see why that should matter. AuthService does not inject AppService, so how is it a circular dependency?

This is my error:

> Uncaught Error: Provider parse errors: Cannot instantiate cyclic
> dependency! ApplicationRef ("[ERROR ->]"): in NgModule AppModule in
> ./AppModule@-1:-1 Cannot instantiate cyclic dependency! ApplicationRef
> ("[ERROR ->]"): in NgModule AppModule in ./AppModule@-1:-1
>     at NgModuleProviderAnalyzer.parse (compiler.js:19550)
>     at NgModuleCompiler.compile (compiler.js:20139)
>     at JitCompiler._compileModule (compiler.js:34437)
>     at eval (compiler.js:34368)
>     at Object.then (compiler.js:474)
>     at JitCompiler._compileModuleAndComponents (compiler.js:34366)
>     at JitCompiler.compileModuleAsync (compiler.js:34260)
>     at CompilerImpl.compileModuleAsync (platform-browser-dynamic.js:239)
>     at PlatformRef.bootstrapModule (core.js:5567)
>     at bootstrap (main.ts:13)

Here is my AppModule:

import { NgModule, APP_INITIALIZER } from '@angular/core';

import { AppService, AppServiceFactory } from './app.service';
import { CoreModule } from 'core';

@NgModule({
    imports: [
        // ...
        CoreModule,
        // ...
    ],
    providers: [
        AppService,
        {
            provide: APP_INITIALIZER,
            useFactory: AppServiceFactory,
            deps: [AppService],
            multi: true
        }
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule {}

Here is the AppService:

import { Injectable } from '@angular/core';

import { finalize } from 'rxjs/operators';

import { AuthService } from 'core/services/auth/auth.service';

export function AppServiceFactory(appService: AppService): () => Promise<any> {
    return () => appService.doBeforeBootstrap();
}

@Injectable()
export class AppService {
    constructor(private authService: AuthService) {}

    doBeforeBootstrap(): Promise<any> {
        return new Promise(resolve => {
            this.authService.isLoggedIn().then((loggedIn: boolean) => {
                // If the user is logged in, resolve (do nothing).
                if (loggedIn) {
                    return resolve();
                }

                // Otherwise, refresh the users token before resolving.
                this.authService.refreshToken().pipe(
                    finalize(() => resolve())
                ).subscribe();
            });
        });
    }
}

Anyone know why such an error occurs?

Edit (dependencies of AuthService):

constructor(
    private ref: ApplicationRef,
    private router: Router,
    private http: HttpClient,
    // Other custom services that are NOT imported into AppService...
) {}
Lansana Camara
  • 9,497
  • 11
  • 47
  • 87
  • Are you providing the `AuthService` somewhere? I see the `AppService` is. Not sure if that not being provided would cause your issue. – R. Richards Mar 19 '18 at 21:09
  • It is provided in the `CoreModule`, which is imported in the `AppModule` in the example above. – Lansana Camara Mar 19 '18 at 21:09
  • What are the dependencies of AuthService ? – Noémi Salaün Mar 19 '18 at 22:27
  • @NoémiSalaün Edited question. – Lansana Camara Mar 19 '18 at 22:31
  • I don't know exactly how it works internally, but maybe the ApplicationRef does not exist before bootstraping the app, and that's why it cannot works, because you try to instantiate the AuthService before the bootstrap – Noémi Salaün Mar 19 '18 at 22:33
  • @NoémiSalaün That's a brilliant idea, however no luck. I could see how that may cause this, though. I thought it might be because `AuthService` is not in the injector yet since the app hasn't been bootstrapped, however I'd expect an error more along the lines of trying to access properties of an undefined variable as opposed to a circular dependency... – Lansana Camara Mar 19 '18 at 22:39

1 Answers1

4

You provide APP_INITIALIZER with a factory that depends on AppService that depends on AuthService that depends on ApplicationRef that depends on ApplicationInitStatus that depends on APP_INITIALIZER... et voila

See https://github.com/angular/angular/blob/5.2.9/packages/core/src/application_init.ts and https://github.com/angular/angular/blob/5.2.9/packages/core/src/application_ref.ts at line 398

Noémi Salaün
  • 4,866
  • 2
  • 33
  • 37
  • Beautiful work, thanks sir. Time to create a proxy service to import into both `AuthService` and `AppService` so the circular dependency can go away. – Lansana Camara Mar 19 '18 at 22:56
  • If a service need some dependencies that can be resolve after the bootstrap, you can inject the Injector, and then get the real dependency later when the app is bootstraped – Noémi Salaün Mar 19 '18 at 22:58