Angular:带有GlobalErrorHandler的APP_INITIALIZER

时间:2017-07-19 18:58:58

标签: angular

在我们的Angular应用程序中,我们需要在初始化应用程序之前从服务器加载配置设置。

APP_INITIALIZER用作such效果很好:

export function loadConfig(config: ConfigService) => () => config.load()

@NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule,
        routes,
        FormsModule,
        HttpModule],
    providers: [
        ConfigService,
        {
            provide: APP_INITIALIZER,
            useFactory: loadConfig,
            deps: [ConfigService],
            multi: true
        }
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

然后我扩展了ErrorHandler来创建一个GlobalErrorHandler并想告诉Angular使用我们的GlobalErrorHandler,所以我提供了ErrorHandler并告诉Angular使用类GlobalErrorHandler。

    providers: [
            ConfigService,
            {
                provide: APP_INITIALIZER,
                useFactory: loadConfig,
                deps: [ConfigService],
                multi: true
            },        
            {
                provide: ErrorHandler,
                useClass: GlobalErrorHandler
            }
    ]

GlobalErrorHandler需要来自配置设置的信息,我需要在设置的承诺解决后实例化GlobalErrorHandler。在上面的设置中,在我向服务器调用配置值之前创建了GlobalErrorHandler。

如何从服务器加载配置值,然后实例化GlobalErrorHandler并使用配置值?

2 个答案:

答案 0 :(得分:2)

以你想要它的方式似乎不可能。 Here is the relevant code实例化ErrorHandler并调用APP_INITIALIZER回调:

  private _bootstrapModuleFactoryWithZone<M>(moduleFactory: NgModuleFactory<M>, ngZone?: NgZone):
      Promise<NgModuleRef<M>> {
    ...
    return ngZone.run(() => {
      const ngZoneInjector =
          ReflectiveInjector.resolveAndCreate([{provide: NgZone, useValue: ngZone}], this.injector);
      const moduleRef = <InternalNgModuleRef<M>>moduleFactory.create(ngZoneInjector);

      !!! ------------- here ErrorHandler is requested and instantiated ------------ !!!
      const exceptionHandler: ErrorHandler = moduleRef.injector.get(ErrorHandler, null);
      if (!exceptionHandler) {
        throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
      }
      ...
      return _callAndReportToErrorHandler(exceptionHandler, () => {
        const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);

       !!! ------------ and here your APP_INITIALIZER callbacks are executed ---------- !!!
        initStatus.runInitializers();
        return initStatus.donePromise.then(() => {
          this._moduleDoBootstrap(moduleRef);
          return moduleRef;
        });
      });
    });
  }

正如您所看到的,在触发回调之前,请求从注射器请求ErrorHanlder。并且之前没有其他方法可以执行服务器请求。

我相信您唯一的选择是在应用程序启动之前发出请求,然后在获得响应后,将ErrorHanlder添加到模块提供程序并引导应用程序:

// app.module.ts
export class AppModule {}

export const decoratorDescriptor = {
  imports: [BrowserModule, FormsModule, ReactiveFormsModule],
  declarations: [AppComponent, DebounceDirective, ExampleComponent],
  providers: [],
  bootstrap: [AppComponent]
};


// main.ts
const http = injector.get(Http);
http.get('assets/configs/configuration.json').subscribe((r) => {
  const ErrorHandler = configureErrorHandler(r.json());
  decoratorDescriptor.providers.push(ErrorHandler);
  const decorated = NgModule(decoratorDescriptor)(AppModule);
  platformBrowserDynamic().bootstrapModule(decorated);
});

请参阅this answer了解如何配置http

答案 1 :(得分:0)

认为答案可能会帮助需要它的人...

我能够通过在GlobalErrorHandler部分中将APP_INITIALIZER作为依赖项来解决此问题。这使GlobalErrorHandler实例化,直到APP_INITIALIZER可用,从而解决了我的问题。

providers: [
            ConfigService,
            {
                provide: APP_INITIALIZER,
                useFactory: loadConfig,
                deps: [ConfigService],
                multi: true
            },        
            {
                provide: ErrorHandler,
                useClass: GlobalErrorHandler,
                deps: [APP_INITIALIZER]
            }
    ]