Angular APP_INITIALIZER不会延迟成功启动过程

时间:2019-03-14 19:32:15

标签: angular

我正在尝试使用APP_INITIALIZER进行Angular延迟增强,直到可以捕获一些环境变量为止。

我已阅读并遵循了许多教程和有关此问题的堆栈溢出答案,但没有一个与我所看到的相符。

APP_INITIALIZER确实可以工作,因为如果诺言被拒绝,它可以阻止页面加载,但是成功则不能。而是,控制台日志记录指示依赖初始化服务的其他服务会在完成之前尝试使用它,并且您可以想象所有的欢笑而不是装入所有不存在的东西。

互联网和文档发誓这项工作有效。这是我的代码:

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

export interface IAppConfigDetails {
  environment: any;
  apiUrls: Array<string>;
  googleAnalyticsEnabled: boolean;
}

export const appEnvironmentFactory = (appConfig: AppConfigService) => {
  return () => {
    return appConfig.loadEnvironment();
  };
};

@Injectable()
export class AppConfigService {
  private appConfig: IAppConfigDetails = {
    environment: {},
    apiUrls: [],
    googleAnalyticsEnabled: false,
  };

  constructor(private http: HttpClient) { }

  loadEnvironment() {
    console.log('loadEnvironment called');

    const promise = new Promise((resolve, reject) => {
      this.http.get('/assets/environment.json')
        .toPromise()
        .then((data: any ) => {
          // console.log('About to set environment');
          // console.log(data);
          this.appConfig.environment = data['environment'];
          this.appConfig.apiUrls = data['API_URLS'];
          this.appConfig.googleAnalyticsEnabled = data['GOOGLE_ANALYTICS_ENABLED'];
          resolve(this.appConfig);
          // console.log('Environment fetched');
        }).catch((error) => {
          console.error(
            'Environment definition not found.' +
            'If you\'re running this locally, try calling make_environment.sh to generate a new environment file.'
          );
          console.log(error);
          reject(error);
        });
    });

    return promise;
  }

  getConfig(): IAppConfigDetails {
    return this.appConfig;
  }
}

,例如,用于:

@Injectable()
export class EnvironmentService {

  private microservices: IMicroservice[] = [];
  private environment: { [index: string]: any } = {};

  constructor(
    protected appConfigService: AppConfigService,
  ) {
    const appConfig = appConfigService.getConfig(); // <- Line 22
    console.log('App config is:');
    console.log(JSON.stringify( appConfig, null, 4 ));
    console.log('App config environment is:');
    console.log(appConfig.environment);
    this.environment = Object.assign({}, appConfig.environment);
    console.log('this.environment:');
    console.log(this.environment);
  }

  ...
}

控制台日志记录指示在环境服务需要它时默认配置了appConfig,并在服务初始化后进行加载。

控制台日志:

App config is:
environment.service.ts:24 {
    "environment": {},
    "apiUrls": [],
    "googleAnalyticsEnabled": false
}
environment.service.ts:25 App config environment is:
environment.service.ts:26 Object
environment.service.ts:28 this.environment:
environment.service.ts:29 Object
app-config.service.ts:27 loadEnvironment called

和提供者参考:

  providers: [
    ...
    AppConfigService,
    {
      provide: APP_INITIALIZER,
      useFactory: appEnvironmentFactory,
      multi: true,
      deps: [AppConfigService]
    }
    ...
  ]

1 个答案:

答案 0 :(得分:0)

我终于在https://github.com/angular/angular/issues/23279中找到了这个问题的吸引力,我真的建议阅读。

作为总结,APP_INITIALISER不保证您的配置服务会首先完成,尤其是如果您将其提供的服务注入子模块中时。通过确保您的应用程序模块不依赖于所提供的服务,显然您可能能够解决此问题(我尚未测试过),这会迫使其他模块过早执行。

但是,如果您绝对需要在Angular使用它之前运行配置服务,最好的方法是修改main.ts以延迟Angular引导。  然后,您可以依靠配置服务来满足您的需求。 (感谢https://github.com/rehfeldchris及其在线程中的注释。)

实践中的一个例子是:

main.ts

function bootstrapFailed(val: any) {
    document.getElementById('bootstrap-fail').style.display = 'block';
    console.error('bootstrap-fail', val);
}

// If the environment is set to boostrap
if ( environment.bootstrapping && environment.bootstrapping.configuration) {
 fetch('assets/configuration.json')
    .then(response => response.json())
    .then(config => {
        if (!config ) {
            bootstrapFailed(config);
            return;
        }

        // Store the response somewhere that your ConfigService can read it.
      (<any>window)['tempConfigStorage'] = config;

        platformBrowserDynamic()
            .bootstrapModule(AppModule)
            .catch(bootstrapFailed);
    })
    .catch(bootstrapFailed);
} else {
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(bootstrapFailed);
}
configuration.service.ts

@Injectable()
export class ConfigurationService {
  /*
  Configuration service is guaranteed to have run before Angular bootstraps itself.
   */
  public appConfig: IAppConfigDetails = {
    environment: {},
    apiUrls: [],
    googleAnalyticsEnabled: false,
  };

  constructor() {
  }

  initConfig( jsonData: any) {
    if (jsonData) {
      this.appConfig.environment = jsonData.environment;
      this.appConfig.apiUrls = jsonData.API_URLS;
      this.appConfig.googleAnalyticsEnabled = jsonData.GOOGLE_ANALYTICS_ENABLED;
    }
  }

  getConfig(): IAppConfigDetails {
    return this.appConfig;
  }
}

export function configurationFactory() {
  const service = new ConfigurationService();
  service.initConfig((<any>window)['tempConfigStorage']);
  return service;
}