如何在 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
) 以允许您想要的行为。
但是,我在阅读库代码时意识到他们接受一个对象和一个承诺!
因此,我创建了一个示例来处理承诺并从我们的服务器 (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;
}
}
}
我的目标是从 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
) 以允许您想要的行为。
但是,我在阅读库代码时意识到他们接受一个对象和一个承诺!
因此,我创建了一个示例来处理承诺并从我们的服务器 (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;
}
}
}