我有一个应用程序,它以两种不同的模式工作:演示和正常。演示模式仅在用户没有帐户且只想尝试应用程序以查看其工作方式时使用。
现在假设我们在应用中使用了一项服务。例如,检索Account
并对用户进行身份验证的服务:
abstract class AccountService {
authenticate(username: string, password: string): Observable<Boolean>;
getAccount(): Observable<Account>;
}
对于此服务,我们有两个实现,每个模式一个。 AccountServiceImpl
,对于正常模式,向某个服务器执行HTTP请求以检索帐户。虽然AccountServiceDemoImpl
只返回一个硬编码的。
假设我们有一个AccountComponent
组件,向用户显示帐户名称。对于这个组件,我们使用哪个实现并不重要,所以我们只在它的构造函数中注入一个AccountService
。
一切都很好,我们可以轻松地使用任一实现来填写AccountComponent。现在,当用户单击登录页面上的按钮时,我们希望能够在运行时在这些实现之间切换。所以我写了一个工厂提供程序来确定使用哪个AccountService实现
{provide: AccountService, deps[AccountServiceImpl, AccountServiceDemoImpl], (regularService, demoService) => demoModeActivated ? demoService : regularService}
可以想象,AccountService
也会在登录页面上注入,以处理用户的身份验证。这意味着工厂已经过评估。现在,问题是此评估的结果缓存在Angular中。因此,当演示模式激活并且用户导航到AccountComponent
时,将再次注入实际的AccountServiceImpl
。
有没有办法清除DI缓存(针对一组特定的DI令牌)?或者我应该尝试以另一种方式处理此功能? (例如,编写委托给AccountService
或AccountServiceImpl
的{{1}}的另一个实现。)
答案 0 :(得分:2)
最初,似乎工厂提供商会为您解决此问题。不幸的是,工厂左轮手枪是同步的,你依靠异步调用来确定要提供的服务。
相反,你可以使用Resolve后卫。在resolve方法中,您将返回一个调用auth方法的observable,然后映射到new up up服务。然后调用first()运算符在第一次运行后完成observable。
它解决了守卫似乎被设计为返回简单的DTO结构,而不是服务。但这很有效。
如果您希望此服务保持单一,则可以在Resolve Guard中注入两个服务并映射到任一个服务,而不是新建实例。
export class AccountServiceResolver implements Resolve<AccountService> {
constructor(private accountService: AccountService, demmoService: AccountDemoService) { }
resolve(): Observable<AccountService> {
return this.accountService.getAccount().map(account => {
if (account.real) {
return this.accountService;
} else {
return this.demoService;
}
}).first();
}
}
然后在您的组件中获取此内容:
ngOnInit(route: ActivatedRoute) {
const service = route.data[0];
}