如何在 Angular 应用程序启动前获取提供程序数据

How to get provider data before Angular application startup

我的目标是从 AppConfigService 异步获取“clientId”,并在应用程序启动前将其用作“GoogleLoginProvider”函数的“clientId”。

我可以将它放在环境变量中,但在我的具体情况下,这不是一个选项。

我正在使用 Angular 8.

import { APP_INITIALIZER } from '@angular/core';

export function getGoogleClientId(appConfigService: AppConfigService) {
    return () => appConfigService.getGoogleClientId().toPromise().then((clientId) => {
        // service reliably returns clientId here
    });
}

const getGoogleId: Provider = {
    provide: APP_INITIALIZER,
    useFactory: getGoogleClientId,
    deps: [AppConfigService],
    multi: true
}

@NgModule({
    providers: [
        {
            provide: 'SocialAuthServiceConfig',
            useValue: {
                autoLogin: false,
                providers: [{
                    id: GoogleLoginProvider.PROVIDER_ID,
                    provider: new GoogleLoginProvider(clientId), //<-- How do I get the service's "clientId" here?
                }],
            } as SocialAuthServiceConfig
        }
    ]
})
export class AppModule {}

您的问题是您正在使用 useValue 注入一个对象,但您需要使用 useFactory 根据 运行 时间之前不可用的信息创建一个可变的依赖值允许依赖项(API 服务、配置服务等)。

然后我建议修改您此时正在使用的库 (angularx-social-login) 以允许您想要的行为。

但是,我在阅读库代码时意识到他们接受一个对象和一个承诺!

You can check It here

因此,我创建了一个示例来处理承诺并从我们的服务器 (API) 获取我们的配置。

app.module.ts

export function AppConfigServiceFactory(
  configService: AppConfigService
): () => void {
  return async () => await configService.load();
}

@NgModule({
  imports: [BrowserModule, FormsModule, SocialLoginModule, HttpClientModule],
  declarations: [AppComponent, HelloComponent],
  bootstrap: [AppComponent],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: AppConfigServiceFactory,
      deps: [AppConfigService],
      multi: true
    },
    {
      provide: "SocialAuthServiceConfig",
      useValue: new Promise(async resolve => {
        // await until the app config service is loaded
        const config = await AppConfigService.configFetched();

        resolve({
          autoLogin: false,
          providers: [
            {
              id: GoogleLoginProvider.PROVIDER_ID,
              provider: new GoogleLoginProvider(config.googleClientId)
            }
          ]
        } as SocialAuthServiceConfig);
      })
    }
  ]
})
export class AppModule {}


app.config.service

export class AppConfigService {
  static config: AppConfig | null = null;

  constructor(private api: ApiService) {}

  static configFetched(): Promise<AppConfig> {
    return new Promise(async resolve => {
      // wait for the app config service is loaded (after 3000 ms)
      const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
      const waitFor = async function waitFor(f) {
        // check each 500 ms
        while (!f()) await sleep(500);
        return f();
      };
      await waitFor(() => AppConfigService?.config); 

      resolve(AppConfigService.config);
    });
  }

  async load(): Promise<AppConfig> {
    try {
      // simulating HTTP request to obtain my config
      const promise = new Promise<AppConfig>(resolve => {

        // after 3000 ms our config will be available
        setTimeout(async () => {
          const config: AppConfig = await this.api.getConfig().toPromise();
          AppConfigService.config = config;
          resolve(config);
        }, 3000);

      }).then(config => config);

      return promise;
    } catch (error) {
      throw error;
    }
  }
}

My complete solution is here on stackblitz.