首页 > 解决方案 > Angular 12 APP_INITIALIZER 未在其他模块之前触发

问题描述

我知道这是一个经常被重复的问题,我已经阅读了几乎所有这些问题,因为我一直试图让 app_initializer 启动几个小时,但无济于事。所以最后的手段是这个

我要做的就是动态加载我的 Spring Boot 应用程序的其余端点,以便我可以部署到多个环境。

我的设置如下:

应用配置服务

.....
loadConfig() : Promise<any> {
  const jsonFile = 'assets/config/config.json'; 
  return this.httpClient
      .get(jsonFile)
      .pipe(tap((result) =>{
        AppConfigService.settings = <IAppConfig>result;
        console.log(AppConfigService.getConfig());

      })).toPromise();

}

应用模块

export function initializeConfigData(appConfigService: AppConfigService) {
  return (): Promise<any> => { 
    console.log("in app init fn");
    return appConfigService.loadConfig2();
  }
}
...
providers: [AppConfigService,
  {
    provide: APP_INITIALIZER, useFactory: initializeConfigData, deps: [AppConfigService], multi: true
  }]

发生的事情是我的应用程序中有多个模块。例如,auth 模块有一个服务,它对 rest 端点进行身份验证调用,并且似乎在应用初始化程序完成之前就触发了。

所以在我加载应用程序后,我最终得到了

Uncaught TypeError: Cannot read property 'root' of undefined

在控制台上(root 是我的配置 json 数据中的关键)

我已经阅读了多篇文章,并在我的 loadConfig 方法中尝试了很多东西,比如尝试返回一个新的 Promise((resolve, reject) =>.... 但没有任何帮助。因为我在 12 岁,我也尝试了 Observables,但没有任何效果。无论我做什么,我的 AuthenticationService 都失败了,表明它无法读取该属性。

我错过了什么?或者这是当您有多个模块时尝试让 app_initializer 工作时的行为。

只是为了测试一下,我尝试了一个简单的 setTimeout(没有读取配置以获取 URL),并且按预期触发。所以这该死的东西着火了,但我认为这是一个时间问题......我在这里束手无策,所以任何帮助都将不胜感激。

谢谢!

标签: angular

解决方案


我测试了上面的场景(使用提供的代码),似乎一切正常(我没有多个模块,只有 app 模块)。我在里面记录配置AppComponent.ngOnInit,它总是在那里有配置值。尽管如此,我想提出另一种方法。您AppConfigService可以使用Observable. 这样,所有其他依赖于配置的东西都会等到它在那里。

@Injectable({
  providedIn: 'root',
})
export class AppConfigService {
  private configSource = new ReplaySubject<IAppConfig>(1);
  // public static config: IAppConfig | null = null;

  public config$: Observable<IAppConfig> = this.configSource.asObservable();

  constructor(private http: HttpClient) {}

  loadConfig = (): Observable<any> => {
    const jsonFile = 'assets/config/config.json';
    return this.http.get(jsonFile).pipe(
      tap((result) => {
        // AppConfigService.config = result;
        this.configSource.next(result);
      })
    );
  };
}

AppModule 看起来与您所拥有的几乎相同(除了我没有添加AppConfigService到 providers 数组中,因为它已经是providedIn: 'root'):

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

每当您需要使用您的配置时,只需注入AppConfigService并执行您的 http 调用:

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  constructor(
    private appConfigService: AppConfigService,
    private http: HttpClient
  ) {}

  getUser(): Observable<any> {
    return this.appConfigService.config$.pipe(
      switchMap((config) => this.http.get(`${config.root.backendUrl}/users`))
      // ...
    );
  }
}

推荐阅读