2

I'm using APP_INITIALIZER hook to load some app configuration from a local json file. After the loaded configuration, I want to use it in another provider (setting BASE_PATH for my generated swagger-codegen services).

I get this error: Uncaught TypeError: Cannot read property 'api' of undefined

The value I try to access AppInitService.settings.api is a static property on AppInitService. I suspect this is the problem, but as I understand the APP_INITIALIZER hook should guarantee to wait before continuing...

How do I wait for the value to be loaded in APP_INITIALIZER and ready to be reused in another providers useValue?

app.module.ts

 export function initializeApp(appConfig: AppInitService) { return (): Promise<any> => appConfig.init() } @NgModule({ declarations: [...], imports: [...], providers: [ AppInitService, { provide: APP_INITIALIZER, useFactory: initializeApp, deps: [AppInitService], multi: true, }, { provide: BASE_PATH, useValue: AppInitService.settings.api, }, ], 

app.config.service.ts

 @Injectable() export class AppInitService { static settings: IAppConfig private conf: IAppConfig constructor(private http: HttpClient) {} public getConf() { return this.conf } init() { const jsonFile = `assets/config/config.json` return this.http .get(jsonFile) .pipe( tap((returnedConfig: IAppConfig) => { console.log('returnedConfig', returnedConfig) AppInitService.settings = returnedConfig this.conf = returnedConfig }) ) .toPromise() } } 
1

1 Answer 1

2

You can use a factory provider.

{ provide: BASE_PATH, useFactory: (service: AppInitService) => service.conf, // doesn't need to be static deps: [AppInitService] } 

Angular will only call the factory function the first time the provider is resolved, but you are just assuming it will be resolve later. It might work for now, but could break later or never work at all.

Instead, use an observable as the injectable value.

@Injectable() public class AppInitService { public conf: ReplaySubject<any> = new ReplaySubject(1); init() { const jsonFile = `assets/config/config.json` return this.http .get(jsonFile) .pipe( tap((returnedConfig: IAppConfig) => this.conf.next(returnedConfig)) ).toPromise() } } 
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you. I can't get it to work though... Why did you use ReplaySubject instead of - say - BehaviorSubject?
@olefrank you don't want the observable to emit until a value is ready. BehaviorSubject will always emit a value, but ReplaySubject won't emit until at least next() is called once.
I use it like this: { provide: BASE_PATH, deps: [AppInitService], useFactory: (service: AppInitService) => service.conf.subscribe(val => val.api.baseUrl), }, But it returns [object object]
Could you please provide an example how to use your suggested solution with ReplaySubject?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.