Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: How to correctly implement APP_INITIALIZER

I have a Angular 5.2.0 application. I looked up how to implement APP_INITIALIZER to load configuration information before the app starts. Here an extract of the app.module:

providers: [
    ConfigurationService,
    {
        provide: APP_INITIALIZER,
        useFactory: (configService: ConfigurationService) =>
            () => configService.loadConfigurationData(),
        deps: [ConfigurationService],
        multi: true
    }
],

Here the configuration.service:

import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Configuration } from './configuration';

@Injectable()
export class ConfigurationService {
    private readonly configUrlPath: string = 'Home/Configuration';
    private configData: Configuration;

    constructor(
        private http: HttpClient,
        @Inject('BASE_URL') private originUrl: string) { }

    loadConfigurationData() {
        this.http
            .get<Configuration>(`${this.originUrl}${this.configUrlPath}`)
            .subscribe(result => {
                this.configData = {
                    test1ServiceUrl: result["test1ServiceUrl"],
                    test2ServiceUrl: result["test2ServiceUrl"]        
                }
            });
    }

    get config(): Configuration {
        return this.configData;
    }
}

Here is an example of a constructor of a component where the configData is used:

export class TestComponent {
    public test1ServiceUrl: string;

    constructor(public configService: ConfigurationService) {
        this.test1ServiceUrl = this.configService.config.test1ServiceUrl;
    }
}

It works fine with all the components which are defined within the <router-outlet></router-outlet>. But the same implementation in a component outside the <router-outlet></router-outlet> does not work.
When I debug the respective constructor of the component where it does not work it says that configService is null.
Why is the APP_INITIALIZER executed before the constructor of a component inside the <router-outlet></router-outlet> is called but not before the constructor of a component outside the <router-outlet></router-outlet>?

like image 935
Palmi Avatar asked Apr 07 '18 13:04

Palmi


2 Answers

Due to how APP_INTIALIZER works, it's expected that asynchronous initializers return promises, but your implementation of APP_INTIALIZER multiprovider doesn't because loadConfigurationData function doesn't return anything.

It should be something like:

loadConfigurationData(): Promise<Configuration> {
  return this.http.get<Configuration>(`${this.originUrl}${this.configUrlPath}`)
  .do(result => {
    this.configData = result;
  })
  .toPromise();
}
like image 156
Estus Flask Avatar answered Oct 09 '22 06:10

Estus Flask


Do something like

 export function StartupServiceFactory(startupService: StartupService) {
      return () => startupService.load();
    }
    const APPINIT_PROVIDES = [
      StartupService,
      {
        provide: APP_INITIALIZER,
        useFactory: StartupServiceFactory,
        deps: [StartupService],
        multi: true,
      },
    ];

Startup service

load():Promise{
    return new Promise(resolve, reject){
        //load all your configuration 
         resolve(); 
    }

  }
like image 40
Pir Abdul Avatar answered Oct 09 '22 07:10

Pir Abdul