简体   繁体   English

角度:具有GlobalErrorHandler的APP_INITIALIZER

[英]Angular: APP_INITIALIZER with GlobalErrorHandler

In our Angular app we need to load config settings from the server before initializing the app. 在我们的Angular应用程序中,我们需要在初始化应用程序之前从服务器加载配置设置。

Using APP_INITIALIZER as such works great: 使用APP_INITIALIZER这样伟大的作品:

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 {
}

I then extended ErrorHandler to create a GlobalErrorHandler and wanted to tell Angular to use our GlobalErrorHandler, so I provided the ErrorHandler and told Angular to use the class GlobalErrorHandler. 然后,我扩展了ErrorHandler以创建GlobalErrorHandler并想告诉Angular使用我们的GlobalErrorHandler,因此我提供了ErrorHandler并告诉Angular使用类GlobalErrorHandler。

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

GlobalErrorHandler needs information from the config settings and I need to instantiate GlobalErrorHandler AFTER the setting's promise is resolved. GlobalErrorHandler需要配置设置中的信息,在设置的承诺得到解决后,我需要实例化GlobalErrorHandler。 In the set up above, GlobalErrorHandler is created before my call is made to the server for the configuration values. 在上面的设置中,在对服务器进行配置值调用之前创建了GlobalErrorHandler。

How can I load the configuration values from the server, and THEN instantiate the GlobalErrorHandler and use the configuration values? 如何从服务器加载配置值,然后实例化GlobalErrorHandler并使用配置值?

It doesn't seem to be possible with the way you want it done. 您想要完成的方式似乎不可能。 Here is the relevant code that instantiates the ErrorHandler and calls APP_INITIALIZER callbacks: 以下是相关的代码 ,用于实例化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;
        });
      });
    });
  }

As you can see the ErrorHanlder is requested from the injector before your callbacks are triggered. 如您所见,触发回调之前,需要从注入器请求ErrorHanlder And there's no other way to perform server request before. 并且之前没有其他方法可以执行服务器请求。

I believe your only option is to make a request before the application starts and then once you get the response add the ErrorHanlder to module providers and bootstrap the app: 我相信您唯一的选择是在应用程序启动之前发出请求,然后在收到响应后将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);
});

See this answer to learn how to configure the http . 请参阅此答案以了解如何配置http

Thought the answer might help someone who needs it... 以为答案可能会帮助需要它的人...

I was able to resolve the issue by giving the APP_INITIALIZER as a dependency in the GlobalErrorHandler section. 通过将APP_INITIALIZER作为依赖项提供给GlobalErrorHandler部分,我能够解决此问题。 This made the GlobalErrorHandler instantiation to wait till the APP_INITIALIZER was available, thus solving my issue. 这使GlobalErrorHandler实例化,直到APP_INITIALIZER可用,从而解决了我的问题。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM