[现在我确定为时已晚,但我会尝试回答这个问题。请注意,尽管 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 之前它不太可能执行) .它有时在AppInitializerFactory之后被执行的事实可能是纯粹的运气。
需要明确的是,使用 APP_INITIALIZER 令牌的提供程序保证在创建第一个 组件 之前被实例化 - 但这与 服务 的顺序无关被实例化。服务之间的依赖关系通过依赖注入树来表达。
请注意,Angular 路由器本身使用APP_INITIALIZER - 路由器的一些初始化发生在那里。因此,通过将路由添加到混音中,您可能会更改(已经是随机的)事情发生的顺序。
要记住的另一件事是,虽然 BaseHrefFactory 依赖于 ConfigServiceBase,但它确实不依赖于“ConfigService 的实例,其 load() 方法已被调用”。注入器会很高兴地向它传递一个对 ConfigService 实例的引用,该实例的 load() 方法没有被调用。
为了解决这一切,我会注入一个服务,而不是尝试注入一个值,如下所示。
1) 创建一个服务来保存该值:
@Injectable()
export class AppBaseHrefService {
public baseHref: string;
}
2) 将AppInitializerFactory函数修改为带AppBaseHrefService参数,并添加一个额外的步骤来根据配置设置值:
export function AppInitializerFactory(config: ConfigServiceBase, appBaseHrefService: AppBaseHrefService) {
return () => config.load()
.then(() => {
appBaseHrefService.baseHref = config.baseHref
});
}
3) 让您的 APP_INITIALIZER 提供程序依赖于 AppBaseHrefService:
{ provide: APP_INITIALIZER, useFactory: AppInitializerFactory, deps: [ConfigServiceBase, AppBaseHrefService], multi: true }
当然,您可能会认为简单地注入现有的ConfigService / ConfigServiceBase 而不是注入另一个服务更容易。我倾向于同意,除非您的实际 ConfigService 有很多配置信息,而您只想公开其中的一部分。
更新 2019 年 7 月,因为似乎没有人能够让它工作......
首先,请记住,我不声称能够设置APP_BASE_HREF 提供的值。相反,我建议您可以注入一些 else 来提供您需要的价值。
我创建了一个StackBlitz 来展示这一点。这里我有以下活动部分:
我在AppModule 中定义了一个InitializerProvider 提供程序,并与APP_INITIALIZER DI 令牌相关联。这使用工厂函数来进行异步初始化。因为该函数返回一个Promise,所以在该承诺得到解决(即初始化完成)之前,不会创建任何组件等。
上面的工厂函数使用ConfigService 异步获取“一些数据”。然后它使用该数据初始化BaseUrlService。可以根据需要在此处添加更多同步或异步操作。 (Async/await 非常适合这些东西)。
组件(例如本例中的 AppComponent)可以注入 BaseUrlService 并使用它来访问从初始化期间获取的数据派生的基本 URL。由于初始化发生在任何组件创建之前,因此不应出现这些组件看到未初始化服务的情况。
HTH。