APP_LASE_HREF令牌的Providerfactory在APP_INITIALIZER完成之前被调用

时间:2018-03-19 12:39:20

标签: angular

我刚刚在我们的软件中发现了一个错误 - 非常确定它曾经工作过,但现在已经坏了。

可以在此处找到再现问题的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可以重现它,但很高兴。

1 个答案:

答案 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具有大量的配置信息,而您只想公开其中的一些信息。