简体   繁体   中英

Angular 6 - Initialize service with async calls before dependency injection

I have a service which based on the environment file, loads up a configuration object in memory. However, the reading of the settings is asynchronous and the application starts before even all the settings are loaded and crashes. Is there any way to 'await' for these functions before the dependency injection is complete.

My service :

import { Injectable } from '@angular/core';
import { IAppConfig } from '../models/app-config.model';

@Injectable()
export class AppConfig {
    settings: IAppConfig;
    version: any;

constructor() {
    this.loadConfig();
}

// reads env.json file
// based on which environment it is loads config setting from
// environment specific config settings.

public loadConfig() {
    return new Promise((resolve, reject) => {
        const envFile = '/env.json';
        this.readJsonFile(envFile).
            then((envData) => {
                const configFile = `assets/appconfigs/config.${envData.env}.json`;
                this.version = envData.version;
                this.readJsonFile(configFile).
                    then((configsettings) => {
                        this.settings = configsettings;
                        resolve(this.settings);
                    });
            });
    });
}

// reads json file and returns the json object promise
public readJsonFile(jsonUrl: string): any {
    return new Promise((resolve, reject) => {
        let retObject: any;
        const xhr = new XMLHttpRequest();
        xhr.overrideMimeType('application/json');
        xhr.open('GET', jsonUrl, true);
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    retObject = JSON.parse(xhr.responseText);
                    resolve(retObject);
                } else {
                    reject(`Could not load file '${jsonUrl}': ${xhr.status}`);
                }
            }
        };
        xhr.send(null);
    });
}

}

I want the settings object to be loaded before the application fires up. The other way was to make this class static and call loadConfig but it is a nightmare for testbeds. Is there anything I could specify when providing the service in the module?

I'm not sure about this solution (untested code), but I guess you could async/await everything.

Is there any reason why you're not using Angular's http service, and instead, making a manual XMLHttpRequest?

It could be something like this :

constructor() {
    this.run()
}

async run() {
    await this.loadConfig()
}

// reads env.json file
// based on which environment it is loads config setting from
// environment specific config settings.

public loadConfig() {
    return new Promise( async (resolve, reject) => {
        const envFile = '/env.json';

        let envData = await this.readJsonFile(envFile)

        const configFile = `assets/appconfigs/config.${envData.env}.json`;

        this.version = envData.version;

        this.settings = await this.readJsonFile(configFile);

        resolve(); // No need to pass this.settings to resolve()
    });
}

// reads json file and returns the json object promise
public readJsonFile(jsonUrl: string): any {
    return this.http
        .get(jsonUrl)
        .map(res => res.json())
        .toPromise()
}

I want the settings object to be loaded before the application fires up.

You could/should make use of the injection token APP_INITIALIZER: angular.io/APP_INITIALIZER

In your case, something like below code would suffice. In your AppModule:

{
  provide: APP_INITIALIZER,
  useFactory: initApp,
  deps: [//add here services which depend on app-config
  ],
  multi: true
}

export function initApp(appConfig: AppConfig) {
  return () => appConfig.loadConfig();
}

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