我有一个可重用组件,
ReusableComponent
,它需要使用包含特定功能的服务。该服务必须实现一个接口 ServiceAbstraction
,以确保它包含该函数签名。我使用令牌在 ReusableComponent
中注入该抽象,因此消费者组件必须定义它:
export const SERVICE_ABSTRACTION_TOKEN = new InjectionToken<ServiceAbstraction>(
'ServiceAbstraction Token'
);
export interface ServiceAbstraction {
doSomething(): void;
}
@Injectable()
export class ServiceImplementation implements ServiceAbstraction {
doSomething() {
console.log('I do something');
}
}
@Component({
selector: 'app-reusable-component',
template: ``,
standalone: true,
})
export class ReusableComponent {
constructor(
@Inject(SERVICE_ABSTRACTION_TOKEN)
private serviceAbstraction: ServiceAbstraction
) {
this.serviceAbstraction.doSomething();
}
}
然后在任何消费者组件中,我可以在其模板中包含这个可重用组件,并利用一个具体服务,该服务使用父组件装饰器的提供者数组来实现该接口。
@Component({
selector: 'app-consumer',
standalone: true,
template: `
<app-reusable-component />
`,
imports: [ReusableComponent],
providers: [
{ provide: SERVICE_ABSTRACTION_TOKEN, useClass: ServiceImplementation },
],
})
export class ConsumerComponent { }
我遇到的问题是,我希望能够在父组件中拥有使用不同服务具体的可重用组件的不同实例。使用角度 DI 可以吗?我知道我可以使用输入将服务传递给可重用组件,但这将是一种反模式,并且我想使用 DI 的角度架构和设计。
这里你有一个堆栈闪电战可以修改。
我认为最接近完全依赖 DI 系统的方法是为每个具体实现提供一个
InjectionToken
。父级需要为可重用组件的每个实例提供特定令牌,该令牌应用于检索正确的具体服务实现。
import { Component, InjectionToken } from '@angular/core';
import { OrderSearchService, UserSearchService } from './services';
import { ISearchProvider, Order, User } from './models';
import { AutoCompleteComponent } from './components/auto-complete.component';
const USER_SEARCH_SERVICE = new InjectionToken<ISearchProvider<User>>('user-search-service');
const ORDER_SEARCH_SERVICE = new InjectionToken<ISearchProvider<Order>>('order-search-service');
@Component({
selector: 'app-parent',
standalone: true,
imports: [AutoCompleteComponent],
providers: [
{
provide: USER_SEARCH_SERVICE,
useClass: UserSearchService,
}, {
provide: ORDER_SEARCH_SERVICE,
useClass: OrderSearchService,
}
],
template: `
<app-auto-complete [token]="USER_SEARCH_SERVICE" />
<app-auto-complete [token]="ORDER_SEARCH_SERVICE" />
`,
})
export class ParentComponent() {}
可重用组件必须延迟注入服务,直到令牌输入得到解析,但如果我们捕获
Injector
,这相当容易做到。
import { inject, InjectionToken, Injector, input, OnInit } from '@angular/core';
import { ISearchProvider } from '../models';
@Component({
selector: 'app-auto-complete',
standalone: true,
})
export class AutoCompleteComponent<T> implements OnInit {
private readonly injector = inject(Injector);
readonly token = input.required<InjectionToken<ISearchProvider<T>>>();
service: ISearchProvider<T> | undefined;
ngOnInit() {
this.searchService = this.injector.get(this.token());
}
}