Angular中如何向带参数的InjectionToken传递组件参数?
问题
我定义了如下InjectionToken:
export const SOURCE_DATA_INJECTION_TOKEN = new InjectionToken<CustomObject[]>('All Source Data', { providedIn: 'root', factory: () => { // 返回全部源数据 return someArrayData // 该数据读取自本地.json文件; } });
组件中直接使用该Token:
@Component({}) export class SourceManager { sourceDataList: CustomObject[] = inject(SOURCE_DATA_INJECTION_TOKEN); }
现在需要升级这个INJECTION_TOKEN,使其根据组件中用户选择的源返回过滤后的数据。组件更新后如下:
@Component({}) export class SourceManager { selectedSource: SourceEnum = SourceEnum.SomeSource1 sourceDataList: CustomObject[] = inject(SOURCE_DATA_INJECTION_TOKEN); }
其中selectedSource可通过UI修改,我希望将该枚举值传入InjectionToken,使其返回过滤后的数据而非全部源数据。我注意到工厂方法可接收参数,请问能否将selectedSource传递给它?
解决方案
不能直接把组件的selectedSource传入InjectionToken的工厂方法,核心原因有两点:
inject()和Token的工厂方法都只在初始化阶段执行一次,后续selectedSource通过UI变更时,不会重新触发工厂方法生成新的过滤数据。- 根级提供的
InjectionToken属于全局注入树的一部分,无法直接访问组件实例的私有状态(比如selectedSource)。
下面是两种更实用的实现方案:
方案一:用Angular信号(Angular 16+推荐)
通过服务管理源数据和选中状态,利用信号的自动响应特性实现数据实时更新:
@Injectable({ providedIn: 'root' }) export class SourceDataService { // 读取本地JSON的原始数据 private readonly rawSourceData = someArrayData; // 存储选中源的信号,初始值设为默认枚举值 readonly selectedSource = signal<SourceEnum>(SourceEnum.SomeSource1); // 计算信号:自动根据选中源过滤数据 readonly filteredSourceData = computed(() => { return this.rawSourceData.filter(item => item.source === this.selectedSource()); }); }
组件中使用:
@Component({}) export class SourceManager { private readonly sourceDataService = inject(SourceDataService); // 直接绑定服务的信号到模板,实现双向同步 selectedSource = this.sourceDataService.selectedSource; // 获取实时过滤后的数据源 sourceDataList = this.sourceDataService.filteredSourceData; // UI切换选中源时调用此方法 onSourceChange(newSource: SourceEnum) { this.sourceDataService.selectedSource.set(newSource); } }
方案二:用RxJS BehaviorSubject(兼容所有Angular版本)
通过可观察对象监听选中状态变化,自动推送过滤后的数据:
@Injectable({ providedIn: 'root' }) export class SourceDataService { private readonly rawSourceData = someArrayData; // 存储选中源的BehaviorSubject,自带初始值 private readonly selectedSource$ = new BehaviorSubject<SourceEnum>(SourceEnum.SomeSource1); // 过滤后的数据源:通过pipe映射自动更新 readonly filteredSourceData$ = this.selectedSource$.pipe( map(selected => this.rawSourceData.filter(item => item.source === selected)) ); // 更新选中源的方法 setSelectedSource(source: SourceEnum) { this.selectedSource$.next(source); } }
组件中使用:
@Component({}) export class SourceManager implements OnInit { private readonly sourceDataService = inject(SourceDataService); selectedSource: SourceEnum = SourceEnum.SomeSource1; sourceDataList: CustomObject[] = []; ngOnInit() { // 订阅过滤后的数据,自动更新组件列表 this.sourceDataService.filteredSourceData$.subscribe(data => { this.sourceDataList = data; }); } onSourceChange(newSource: SourceEnum) { this.selectedSource = newSource; this.sourceDataService.setSelectedSource(newSource); } }
备选:基于组件实例提供Token(局限性大)
如果一定要保留InjectionToken的使用方式,可以为每个组件实例单独提供Token,依赖组件的selectedSource:
// 定义过滤数据的工厂函数 export function sourceDataFactory(selectedSource: SourceEnum) { const rawData = someArrayData; return rawData.filter(item => item.source === selectedSource); } // 组件内提供专属的Token实例 @Component({ providers: [ { provide: SOURCE_DATA_INJECTION_TOKEN, useFactory: () => sourceDataFactory(inject(SourceManager).selectedSource), deps: [SourceManager] } ] }) export class SourceManager { selectedSource: SourceEnum = SourceEnum.SomeSource1; sourceDataList: CustomObject[] = inject(SOURCE_DATA_INJECTION_TOKEN); // 注意:这种方式下选中源变更后,需要手动更新数据 onSourceChange(newSource: SourceEnum) { this.selectedSource = newSource; this.sourceDataList = sourceDataFactory(newSource); } }
这种方式的问题是工厂方法仅在组件初始化时执行一次,无法自动响应selectedSource的变更,需要手动调用更新逻辑,仅适合简单场景。
内容的提问来源于stack exchange,提问作者itsji10dra
相关产品推荐
相关产品推荐

