Use fetched config in forRoot
Asked Answered
V

2

10

I'm writing an angular app which uses @ngx-translate. With TranslateModule.forRoot(...) i provide a TranslateLoader:

@NgModule({
  imports: [
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient, ConfigService, LogService]
      }   
    })     
  ]
})

Also i have a ConfigService which loads an config.json utilizing APP_INITIALIZER.

The problem is, the TranslateLoader needs an url from the config. But forRoot() runs before APP_INITIALIZER which leads to ConfigService not having loaded the config and an empty url.

Is there another way to do this?

Currently i'm thinking about manually bootstrapping angular.

Vassal answered 5/3, 2018 at 15:42 Comment(1)
Can you include the code for HttpLoaderFactory class?Rigel
I
5

For anyone still looking at this, I found a way to load translations using the TranslateLoader provider after App Init. The ngx-translate lib allows you to override the current loader. So we can pass a new factory after the app has completed bootstrapping.

export function HttpLoaderFactory(handler: HttpBackend, valueAvailableAfterInit) {
  const http = new HttpClient(handler);
  return new TranslateHttpLoader(http, valueAvailableAfterInit, '.json');
}

export class AppModule {
  constructor(
    private translate: TranslateService,
    private handler: HttpBackend
  ) {}

  ngDoBootstrap() {
    const valueAccessableAfterBootstrap = `I'll leave this to your use-case. For me it is an environment variable overwritten via ngOnInit in app.component`;

    this.translate.currentLoader = HttpLoaderFactory(this.handler, valueAccessableAfterBootstrap); // replace loader
    this.translate.reloadLang('en-US').pipe(take(1)).subscribe(); // reload translations with new loader
  }
}
Importation answered 21/8, 2019 at 4:37 Comment(0)
S
0

I think the following solution is simpler, since you don't need to reload the translations:

@NgModule({
  imports: [
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient, AppConfigService],
      },
    })
  ]
})

export function HttpLoaderFactory(http: HttpClient, appConfigService: AppConfigService) {
  return new CustomTranslateHttpLoader(http, appConfigService, '', `?v=${environment.version}`);
}

export class CustomTranslateHttpLoader extends TranslateHttpLoader {
  constructor(http: HttpClient,
    private readonly appConfigService: AppConfigService,
    prefix?: string,
    suffix?: string) {
    super(http, prefix, suffix);
  }

  override getTranslation(lang: string): Observable<Object> {
    this.prefix = `${this.appConfigService.getConfig().apiUrl}api/translations/`;

    return super.getTranslation(lang);
  }
}

You can retrieve your config values after the app is initialized, as soon as getTranslation is called.

You could also resolve the translation service and make the http call to the endpoint. This only works if your services resolve the api url as well.

export class CustomTranslateHttpLoader extends TranslateLoader {
  constructor(private readonly injector: Injector) {
    super();
  }

  override getTranslation(lang: string): Observable<Object> {
    const translationService = this.injector.get(TranslationsService);

    return translationService.getAllTranslations(lang, environment.version)
      .pipe(map(response => {
        return response as Object;
      }));
  }
}
Spindrift answered 25/11, 2022 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.