angularinterceptorangular9

Angular 9: interceptor cyclic dependency while injecting lazy loaded service


I face Cannot instantiate cyclic dependency! InjectionToken HTTP_INTERCEPTORS error while injecting my lazy loaded service in the interceptor.

However, so strange my problem is being solved by adding an empty setTimeout(() to the service injection in the interceptor. I am wondering why that happens though...

the lazy loaded LanguageService:

import { HttpClient } from '@angular/common/http';
@Injectable()

export class LanguageService {

  constructor(private http: HttpClient,
    private _translate: TranslateService) {
    this.getLanguages();
  }
// ....

the interceptor:

import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { LanguageService } from '../app/blog/services/language.service';

@Injectable()

export class LanguageInterceptor implements HttpInterceptor {
    private _languageService:LanguageService;

    constructor(private injector: Injector) {
        /*  This way Works!
            setTimeout(() => {
            this._languageService = this.injector.get(LanguageService)
         })*/

      
            this._languageService = this.injector.get(LanguageService)
         
    }
    
    intercept(req: HttpRequest<any>, next: HttpHandler) {
    

the interceptor barrel:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { LanguageInterceptor } from './lang-interceptor';

export const httpInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: LanguageInterceptor, multi: true }
];

the shared module

import { NgModule } from '@angular/core';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient, HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { httpInterceptorProviders } from 'src/utilities/interceptor-barrel';
import { ShareModule } from '@ngx-share/core';
import { FormsModule, ReactiveFormsModule }   from '@angular/forms';

export function HttpLoaderFactory(http: HttpClient) {
    return new TranslateHttpLoader(http, '../assets/i18n/', '.json');
}

const MODULES = [
    HttpClientModule,
    HttpClientJsonpModule,
    CommonModule,
    RouterModule,
    ShareModule,
    FormsModule,
    ReactiveFormsModule
];

@NgModule({
    imports: [
        MODULES,
        TranslateModule.forRoot({
            defaultLanguage: 'en',
            loader: {
                provide: TranslateLoader,
                useFactory: (HttpLoaderFactory),
                deps: [HttpClient]
            }
        }),
    ],
    exports: [
        MODULES,
        TranslateModule
    ],
    declarations: [],
    providers: [
        httpInterceptorProviders
    ],
})

the lazy loaded module:

 // ....
imports: [
        SharedModule
    ]
 // ....
 providers: [
        LanguageService
    ]

Solution

  • Your LanguageService should not be added to the providers of the lazy loaded module. It should not be added to any providers array of a module. It should have a providedIn: 'root' in the decorator:

    @Injectable({ providedIn: 'root' })
    export class LanguageService {}
    
    //  lazy loaded module
    imports: [
      SharedModule
    ]
    
    // ....
    providers: []
    
    

    Interceptors on the other hand, should either be declared in your core module, which is included in the App root once. Or in a lazy loaded module, to define a interceptor just for that lazy loaded module


    You should restructure your application. Split up your SharedModule to -only- (!important) include components/pipes/directives. These are things that should go in a SharedModule. What -definitely- should not go in there, are application related services and imports.

    By the looks of it, your SharedModule should be a CoreModule, and only be imported (not exported) in your AppModule. You should -not- import this module in a lazy loaded module. In your case it will redeclare the HttpClientModule -without- the interceptors, because they are declared in the root and not in the lazy module, making you have the issues you're facing.

    Angular itself has a good overview of what kind of modules there are, and what they should and should not contain, and where they should be or should not be imported:

    There are five general categories of feature modules which tend to fall into the following groups:

    • Domain feature modules.
    • Routed feature modules.
    • Routing modules.
    • Service feature modules.
    • Widget feature modules.