ngx-translate / core

The internationalization (i18n) library for Angular
MIT License
4.51k stars 575 forks source link

forChild() overrides forRoot() providers #1278

Open plalx opened 3 years ago

plalx commented 3 years ago

I'm not sure whether this should be considered a bug or not, but I find it VERY misleading that forChild() seems to be overriding providers defined with forRoot() even though no explicit overrides have been specified.

I'm setting up the TranslateModule for all tests (getTestBed().initTestEnvironment(...)) with the following module:

@NgModule({
  imports: [
    TranslateModule.forRoot({
      defaultLanguage: 'en-CA',
      useDefaultLang: true,
      missingTranslationHandler: {
        provide: MissingTranslationHandler,
        useClass: ThrowOnMissingTranslation
      },
      loader: {
        provide: TranslateLoader,
        useClass: TestingTranslateLoader,
      }
    }),
    RouterTestingModule
  ],
  exports: [TranslateModule, RouterTestingModule]
})
export class BaseTestingModule {}

Then in a component test I was trying to load the component's parent module (which uses TranslateModule.forChild()) and noticed that the root loader and missingTranslationHandler were not picked-up or being overriden.

Looking at forChild's implementation we can see that it overrides the loader and missingTranslationHandler even though none were provided as argument.

config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler},

I'm not sure if it's possible (new to Angular), but I would expect forChild providers to default to parent/root values when none provided. I guess one way of doing that could be to omit a default provider entirely? Anyway, I know I could generally just use TranslateModule and dropping forChild(), but in this case the forChild() call is in a third-party library so it's not an option...

Any workarounds?

FortinFred commented 3 years ago

@plalx I've made that very same observation to finally understand where I was going wrong.

AFAIK, the forChild() function is to be called only from a lazy loaded module which will have his own injector. Any feature module that are eagerly loaded will be provided in the root injector.

In other words, calling forRoot() and forChild() in modules that are all loaded eagerly loaded during application bootstrapping will overriding each other.

eulersson commented 3 years ago

For lazy-loaded modules with different translation loaders (loading .json from different files) it seems to be either (in the case of the lazy-loaded):

It's like I can't blend the two.

I got pretty close though maybe you could have a look and play within StackBlitz: https://stackblitz.com/edit/translations-and-lazy-loading?file=README.md