【问题标题】:How to get provider data before Angular application startup如何在 Angular 应用程序启动之前获取提供者数据
【发布时间】:2021-06-07 16:52:59
【问题描述】:

我的目标是从 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 {}

【问题讨论】:

  • 您好,this solution可以帮到您吗?
  • @sohaieb 我想我已经在使用这个解决方案来获取 clientId。我的问题是,一旦我有了 clientId,如何将它放入 GoogleLoginProvider?
  • 这个来源怎么样:Dynamic Dependency Injection?在这个解释中,您可以为您的 clientId 注入 http 请求并从 apiFactory 方法获取结果..
  • @sohaieb 我认为这个解释让我处于同样的境地。该解释显示了如何动态添加类而不是提供者本身或提供者的一部分。如果您查看我的代码,您会发现我没有添加类。我需要在注入之前或引导应用程序之前添加一个提供程序的 id。

标签: angular angular-module


【解决方案1】:

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

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

但是,我在阅读库代码时发现它们接受了一个对象和一个承诺!

You can check It here

因此,我创建了一个示例来处理 Promise 并从我们的服务器 (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.

【讨论】:

  • 我认为这是可行的,但它给出了这个错误,“缺少必需的参数 'client_id'”,但仅当应用程序首次启动时本地存储中没有任何内容。据我所知,“提供:”SocialAuthServiceConfig“”行在“AppConfigServiceFactory”返回之前运行。这也发生在你的 stackblitz 代码中。
  • 我明白了,但我解释的方式相同。该库使用useValue 为对象提供谷歌密钥ID。实现您想要的正确方法是使用工厂来提供价值。我们可以尝试使用另一种方式来处理我们面临的异步问题。但可能是一个有点糟糕的解决方案。
  • 但是,我编辑并更改了行为以使用 useValue 并处理我们遇到的异步问题。
  • 我尝试使用 useFactory 但我无法让它工作。我明白你说的有点糟糕的意思,但也许它会在我们的情况下起作用。
  • 我刚刚实施了您的解决方案,我认为它正在工作。谢谢!
猜你喜欢
  • 1970-01-01
  • 2018-07-27
  • 2018-09-24
  • 2011-02-27
  • 2020-05-02
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 2014-10-13
相关资源
最近更新 更多