我刚刚在我们的软件中发现了一个错误 - 非常确定它曾经工作过,但现在已经坏了。
可以在此处找到再现问题的Plunkr:https://embed.plnkr.co/BNRcQFWGkkEitxjwE7pi/
在我们的app.module中,我们在三个不同的工厂中为APP_INITIALIZER,APP_BASE_HREF和ErrorHandler设置了提供程序
AppInitializerFactory
为我们的配置调用异步加载方法,该方法存储在json文件中。 在此示例中,加载 baseHref 和 submitToSentry
的ErrorHandler
提交给Sentry的自定义错误处理程序,如果config告诉它。 在构造函数中配置尚未加载 - 这是可以接受的。关于必须首先设置ErrorHandler或APP_INITIALIZER的事情。他们必须选一个。相反,我们会在发生错误时查看配置 - 如果我们应该提交Tosentry - 我们会这样做。
BaseHrefFactory
旨在将APP_BASE_HREF注入令牌设置为Config中的任何baseHref。 正如Plunkr所示,baseHref打印最终未定义 - 而不是从config.json预期的 myBaseHref 。
如果...我在app.module.ts:52中注释掉AppRoutingModule - 那么它可以工作。
问题是......(因为我需要AppRoutingModule)
我想我再次(与错误处理程序一样)处于设置路由和初始化应用程序之间的竞争状态 - 在@ angular / router / routerModule内部某处注入了APP_BASE_HREF。
有没有办法实现APP_INITIALIZER阻止路由模块直到完成?
我只是抱错了吗?订购或类似的东西是否可以解决这个问题。
希望有人能够解释我 - 我已经用了几个小时自己调试 - 而且目前已经丢失了。
虽然Plunkr可以重现它,但很高兴。
答案 0 :(得分:0)
[我确定现在为时已晚,但是我可以回答这个问题。请注意,尽管plunkr仍显示代码,但实际上并没有运行-就像当今SO上大多数plunkr示例一样]
您的提供程序设置具有:provide: APP_INITIALIZER, useFactory: AppInitializerFactory, deps: ConfigServiceBase
,尽管从函数声明中并不清楚(因为您对返回值的类型不是很明确),AppInitializerFactory
返回一个函数会返回一个承诺。这很重要,因为Angular初始化过程将等待该承诺得到解决,然后再继续加载其余的应用程序(加载初始组件等)。
您也有provide: APP_BASE_HREF, useFactory: BaseHrefFactory, deps: ConfigServiceBase
。但是,由于这与APP_INITIALIZER
没有关联,因此Angular对BaseHrefFactory
的执行时间没有特别的保证(尽管只有在有人要求执行{ APP_BASE_HREF
)。 {em>有时在AppInitializerFactory
之后被执行的事实可能很幸运。
要清楚,保证使用APP_INITIALIZER
令牌的提供者必须在创建第一个组件之前实例化-但这与服务的顺序无关被实例化。服务之间的依赖性通过依赖性注入树来表示。
请注意,Angular路由器本身使用APP_INITIALIZER
-路由器的某些初始化在此处进行。因此,有可能通过将路由添加到混合中来更改事情发生的顺序(已经是随机的)。
要记住的另一件事是,尽管BaseHrefFactory
依赖于ConfigServiceBase
,但不是依赖于“ {{1}的实例}的ConfigService
方法已被调用”。注入器将很高兴将其引用传递给load()
实例的引用,该实例的ConfigService
方法未调用 。
要解决所有这些问题,我将注入 service 而不是尝试注入值,如下所示。
1)创建一个保存值的服务:
load()
2)修改@Injectable()
export class AppBaseHrefService {
public baseHref: string;
}
函数以使用AppInitializerFactory
参数,并添加一个额外的步骤以根据配置设置值:
AppBaseHrefService
}
3)为您的export function AppInitializerFactory(config: ConfigServiceBase, appBaseHrefService: AppBaseHrefService) {
return () => config.load()
.then(() => {
appBaseHrefService.baseHref = config.baseHref
});
提供者提供对APP_INITIALIZER
的依赖性:
AppBaseHrefService
当然,您可能会决定,简单地注入现有的{ provide: APP_INITIALIZER, useFactory: AppInitializerFactory, deps: [ConfigServiceBase, AppBaseHrefService], multi: true }
/ ConfigService
比注入其他服务更容易。我倾向于同意,除非您实际的ConfigServiceBase
具有大量的配置信息,而您只想公开其中的一些信息。