简体   繁体   中英

Angular Circular dependency when inject TranslateService to interceptor

I have a problem with injecting dependencies into the interceptor. I want to inject TranslateService into HttpErrorInterceptor, but I get a cyclic dependency error. When I remove the TranslateService injection it all works.

I have declared interceptor in my app.module.ts. My app module look like this:

@NgModule({
 declarations: [
   AppComponent
 ],
 imports: [
   BrowserModule,
   BrowserAnimationsModule,
   CoreModule,
   HttpClientModule,
   TranslateModule.forRoot({
   loader: {
      provide: TranslateLoader,
      useFactory: HttpLoaderFactory,
      deps: [HttpClient],
   },
   defaultLanguage: 'pl-pl'
 }),
   AppRoutingModule,
   RouterModule,
   FormsModule,
   ReactiveFormsModule,
   ToastrModule.forRoot()
 ],
 providers: [
   {
     provide: HTTP_INTERCEPTORS,
     useClass: JwtInterceptor,
     multi: true
   },
   {
     provide: HTTP_INTERCEPTORS,
     useClass: HttpErrorInterceptor,
     multi: true,
     deps: [TranslateService, ToastrService]
   }
 ],
 bootstrap: [AppComponent]
})
export class AppModule { }

In AppModule I have imported CoreModule, where I have a folder with interceptors and my CoreModule looks like this:

@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ],
  providers: [
    CookieService,
    NoAuthGuard,
    AuthGuard
  ]
})
export class CoreModule { }

I put the login page in AuthModule, which looks like this:

@NgModule({
  declarations: [LoginComponent, AuthComponent, ForgotPasswordComponent],
  imports: [
    CommonModule,
    AuthRoutingModule,
    RouterModule,
    SharedModule
  ],
  providers: [
    AuthService
  ]
})
export class AuthModule { }

In Authmodule I have imported SharedModule, in which I have TranslateModule exported. And SharedModule look like this:

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    HttpClientModule,
    ReactiveFormsModule
  ],
  exports: [
    TranslateModule,
    ReactiveFormsModule
  ]
})
export class SharedModule { }

I can't find out why I have a cyclic dependency error on the login page.

My assumption is that I have imported CoreModule into AppModule, where I keep interceptors, guards and I have SharedModule, which improvises to all functional modules and I want to keep eg common components there.

Błąd, jaki dostaję to:

core.js:6162 ERROR Error: NG0200: Circular dependency in DI detected for InjectionToken HTTP_INTERCEPTORS. Find more at https://angular.io/errors/NG0200
    at throwCyclicDependencyError (core.js:216)
    at R3Injector.hydrate (core.js:11381)
    at R3Injector.get (core.js:11205)
    at HttpInterceptingHandler.handle (http.js:1978)
    at MergeMapSubscriber.project (http.js:1114)
    at MergeMapSubscriber._tryNext (mergeMap.js:44)
    at MergeMapSubscriber._next (mergeMap.js:34)
    at MergeMapSubscriber.next (Subscriber.js:49)
    at Observable._subscribe (subscribeToArray.js:3)
    at Observable._trySubscribe (Observable.js:42)

The issue you have is that for the initialization of the TranslateModule you depend on the HttpClient which mean the HttpClientModule needs to be initialized first. This causes the initialization of your HttpErrorInterceptor because interceptors are initialized with the HttpClientModule initialization. This causes a cyclic dependency since your interceptor needs the TranslateService . You can workaround this by injecting the Injector in your HttpErrorInterceptor and then request the TranslateService on demand directly from the injector at the time you need it. This way you prevent the cyclic dependency on the initial initialization.

Since you did not provide code for your interceptor here is a sample interceptor that uses this approach.

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  constructor(private readonly injector: Injector) {}

  public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    try {
      const translateService = this.injector.get(TranslateService)
      // log using translate service
    } catch {
      // log without translation translation service is not yet available
    }
  }
}

You still need to handle the case that getting the translate service fails since you can get an error on loading the translations.

According to this GitHub issue, some - myself included - were able to work around the problem by removing the defaultLanguage in TranslateModule.forRoot()

I have implemented my LanguageModule as follows:

@NgModule({
  imports: [
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: (createTranslateLoader),
        deps: [HttpClient]
      },
      isolate: true
    })
  ],
  providers: [
    TranslateService
  ]
})
export class LanguageModule {
  public constructor(translateSvc: TranslateService, http: HttpClient) {
    translateSvc.onLangChange
      .pipe(
        switchMap((currentLang: LangChangeEvent) => zip(
          of(currentLang),
          http.get(`path/to/i18n/${currentLang.lang}.json`)
        ))
      ).subscribe(([currentLang, localizations]) => {
        translateSvc.setTranslation(translateSvc.currentLang, localizations, true);
      });

    translateSvc.use(translateSvc.getDefaultLang());
  }

And then imported it in my CoreModule :

@NgModule({
    imports: [
      CommonModule,
      HttpClientModule,
      BrowserAnimationsModule,
      LanguageModule,
      ...
    ],
    exports: [],
    declarations: [],
    providers: [
      ...
      {
        provide: HTTP_INTERCEPTORS,
        useClass: AuthInterceptor,
        multi: true
      }
    ]
  })
  export class CoreModule {
    public constructor(@Optional() @SkipSelf() parentModule: CoreModule, private translateSvc: TranslateService) {
      this.translateSvc.use(environment.defaultLang)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM