angular - Angular 导入的模块不等待 APP_INITIALIZER
问题描述
我正在尝试auth0/auth0-angular
在 Angular 11 应用程序中使用该库。
我正在关注动态加载配置部分。
它提供了这个示例应用程序模块代码:
// app.module.ts
// ---------------------------
import { AuthModule, AuthClientConfig } from '@auth0/auth0-angular';
// Provide an initializer function that returns a Promise
function configInitializer(
handler: HttpBackend,
config: AuthClientConfig
) {
return () =>
new HttpClient(handler)
.get('/config')
.toPromise()
.then((loadedConfig: any) => config.set(loadedConfig)); // Set the config that was loaded asynchronously here
}
// Provide APP_INITIALIZER with this function. Note that there is no config passed to AuthModule.forRoot
imports: [
// other imports..
HttpClientModule,
AuthModule.forRoot(), //<- don't pass any config here
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: configInitializer, // <- pass your initializer function here
deps: [HttpBackend, AuthClientConfig],
multi: true,
},
],
简而言之,它使用APP_INITIALIZER
提供程序通过 a 动态加载配置Promise
,这应该在 Auth0 库AuthModule
被实例化之前完成,以便它具有从 API 加载的适当 Auth0 配置值,并AuthClientConfig.set(...)
已提前使用这些值调用。
如果这些函数中的任何一个返回 Promise,则在解决 Promise 之前初始化不会完成。
所以,他们的例子从表面上看是有道理的。
但是,当我尝试在自己的应用程序中实际实施此解决方案时,出现以下错误:
Error: Configuration must be specified either through AuthModule.forRoot or through AuthClientConfig.set
这表明AuthModule
在加载和设置配置之前已经实例化了。
在我看来,Angular 在开始实例化导入的模块之前实际上并没有等待Promise
解决。
我认为这个StackBlitz 演示在没有任何 Auth0 依赖项的简化示例中演示了该问题。
在这个例子中,我希望TestModule
直到Promise
解决之后才会实例化,所以我应该看到以下控制台输出:
Inside factory method
Inside promise
Inside timeout
TestModule constructor
但我实际看到的是这样的:
TestModule constructor
Inside factory method
Inside promise
Inside timeout
有人可以帮我理解 的确切性质APP_INITIALIZER
,即它何时被调用,Angular 何时等待Promise
解析,Angular 何时开始实例化其他模块,为什么我的 Auth0 设置无法正确加载等?
解决方案
TL;DR - 我最终通过main.ts
在引导应用程序之前加载配置然后通过自定义注入令牌使配置可用然后我的应用程序配置服务不需要等待它通过 HTTP 加载它已经解决了这个问题可用的。
细节
AppConfig
我的界面片段:
export interface AppConfig {
auth: {
auth0_audience: string,
auth0_domain: string,
auth0_client_id: string,
};
}
InjectionToken
我的常量文件中的自定义:
const APP_CONFIG: InjectionToken<AppConfig>
= new InjectionToken<AppConfig>('Application Configuration');
main.ts
:
fetch('/config.json')
.then(response => response.json())
.then((config: AppConfig) => {
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic([
{ provide: APP_CONFIG, useValue: config },
])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
});
然后在我的 main 中,AppModule
我在没有配置的情况下导入 Auth0AuthModule.forRoot()
并调用我自己的AppConfigService
来配置AuthModule
.
我仍然需要APP_INITIALIZER
依赖AppConfigService
并返回 aPromise
以某种方式使 Angular 等到AppConfigService
构造函数被调用,但否则它什么都不做(并且仍然不会延迟AuthModule
初始化),所以我只是立即解决它。
AppModule
:
@NgModule({
declarations: [
...
],
imports: [
AuthModule.forRoot(),
...
],
providers: [
AppConfigService,
{
provide: APP_INITIALIZER,
useFactory: () => () => {
return new Promise(resolve => {
resolve();
});
},
deps: [ AppConfigService ],
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: AuthHttpInterceptor,
multi: true,
},
],
bootstrap: [ AppComponent ],
})
export class AppModule { }
最后,AppConfigService
:
@Injectable()
export class AppConfigService {
constructor(
@Inject(APP_CONFIG) private readonly appConfig: AppConfig,
private authClientConfig: AuthClientConfig,
) {
this.authClientConfig.set({
clientId: this.appConfig.auth.auth0_client_id,
domain: this.appConfig.auth.auth0_domain,
audience: this.appConfig.auth.auth0_audience,
httpInterceptor: {
allowedList: [
...
],
},
});
}
}
这一切似乎都很好,尽管我仍然不了解它的确切性质,APP_INITIALIZER
并且我不太高兴set
在构造函数中调用 Auth0 客户端配置的方法,而不是像文档建议的那样使用异步“加载”方法。
推荐阅读
- javascript - 我无法使用变量在剑道多选上设置值
- numpy - 将图像转换为 rgb 数组并使用 python 将其转换为 CMYK 数组
- python - Python:锁定目录
- linux - 尝试从 gnu make 中的命令获取值时出错
- odoo - 如何在odoo10电商模块中添加自定义逻辑?我应该继承odoo10中的哪个模型来扩展电子商务购物车逻辑?
- reactjs - 禁用 admin-on-rest SimpleForm 的自动完成功能
- javascript - 在 ng 类的三元组中执行替换操作
- symfony - 如何更改 Symfony 中的错误消息
- javascript - 如何在 Vuex 中深度克隆状态并回滚?
- java - 如何使用java将sqlite的密码设置为连接?