我注意到我的项目有很多 resolver 服务,尽管他们使用不同的实体和存储库来托管相同的代码。所以我接受了挑战,使用泛型将它们减少为单个解析器服务:
@Injectable()
export class DetailResolver<T, R extends Repository<T>> implements Resolve<T> {
constructor(private repositoryService: R, private router: Router) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> {
// use repositoryService to resolve this route
}
}
可以指定实体类型T和存储库类型R.但是我无法使用此服务:
const appRoutes: Routes = [
// other routes
{
path: ':id',
component: MessageEditComponent,
resolve: {
message: DetailResolver<Message, MessageService>
}
}
];
@NgModule({
imports: [
RouterModule.forChild(appRoutes)
],
exports: [
RouterModule
],
providers: [
DetailResolver<Message, MessageService>
]
})
一旦我在DetailResolver<Message, MessageService>
中指定泛型类型,编译器就要我创建一个实例:
类型typeof DetailResolver的值不可调用。你的意思是 包括新的?
我对angular4 DI内部结构并不熟悉。任何人都可以描述什么是错的,这个问题有没有解决办法?我正在使用棱角4.3。
答案 0 :(得分:7)
当您使用注入器注册提供者时,DI系统使用该令牌 - 或者可能被视为键的令牌 - 来维护每次引用时引用的令牌提供者映射要求依赖。
标记通常是唯一且具有符号性的对象,因此泛型的概念在该区域中直接冲突,因为该标记将用作键。泛型是一个设计时工件,这是另一种说法,它们将从生成的JavaScript中消失,因此不会留下他们的类型信息供Angular在运行时查找。出于同样的原因,TypeScript接口也不是有效的令牌。
抽象类
我的第一个选择是使用abstract
classes。抽象类解决了两个问题:
1)虽然它们不能直接实例化,但与接口不同,它们可以包含实现细节,因此可以编译成Javascript。
2)您通过基于扩展的架构获得DI令牌,这可能对您的架构非常有效。
在您的情况下,您可以执行以下操作:
@Injectable()
export abstract class DetailResolver<T, R extends Repository<T>> implements Resolve<T> {...}
@Injectable()
export class MessageResolver extends DetailResolver<Message, MessageService> {...}
然后在NgModule
中,您将按以下方式提供:
providers: [{ provide: DetailResolver, useClass: MessageResolver }]
<强> InjectionToken 强>
另一种选择是使用InjectionToken
(在Angular 4.0之前称为OpaqueToken
)。 InjectionToken
是专门用作DI令牌的对象;然而,与他们的前任OpaqueToken
不同,他们需要输入他们将注入的价值类型。
const MESSAGE_RESOLVER = new InjectionToken<DetailResolver<Message, MessageService>>('MESSAGE_RESOLVER');
您会注意到,您可以在创建InjectionToken
实例时提供类型,以便编译器在此过程中为您提供支持。当您在NgModule
。
providers: [{ provide: MESSAGE_RESOLVER, useClass: MessageResolver }]
值得注意的是useClass
在技术上并不需要;在提供相应的内容时,useValue
或useFactory
等任何其他替代方案均有效。
希望这有用,并就此事提供一些说明!