如何在同一Angular组件中提供同一可注入类的两个实例(新Resource)
Angular服务多实例创建解决方案
问题背景
当前SelectItemGroupsResourceFacade通过providedIn: 'root'注册为单例服务,在同一组件中多次调用configureResource时,后一次的配置会覆盖前一次的参数,导致两个资源引用指向同一配置的资源。需要创建该服务的多个独立实例,而非共享单例。
原服务代码:
@Injectable({ providedIn: 'root' }) export class SelectItemGroupsResourceFacade { $assetPath = signal<string>('') $appName = signal<string>('') #selectItemGroupsResource: ResourceRef<TSelectItemGroup[] | undefined> = resource<TSelectItemGroup[], TSelectItemGroupsLoaderRequest>({ request: () => ({ appName: this.$appName(), assetPath: this.$assetPath() }), loader: selectItemGroupsLoader() }) configureResource = ( appName: string, assetPath: string ): ResourceRef<TSelectItemGroup[] | undefined> => { this.$appName.set(appName) this.$assetPath.set(assetPath) return this.#selectItemGroupsResource } }
组件中调用代码:
#resrcFacade = inject(SelectItemGroupsResourceFacade) $titlesResrc: ResourceRef<TSelectItemGroup[] | undefined> = this.#resrcFacade.configureResource('registration', 'titles.json') $suffixResrc: ResourceRef<TSelectItemGroup[] | undefined> = this.#resrcFacade.configureResource('registration', 'suffixes.json')
解决方案
方法1:手动创建注入器生成独立实例
利用Injector.create()手动创建新的注入器,每次生成全新的服务实例,避免单例共享。
首先修改服务,新增公共方法暴露内部资源(私有成员无法直接访问):
@Injectable({ providedIn: 'root' }) export class SelectItemGroupsResourceFacade { $assetPath = signal<string>('') $appName = signal<string>('') #selectItemGroupsResource: ResourceRef<TSelectItemGroup[] | undefined> = resource<TSelectItemGroup[], TSelectItemGroupsLoaderRequest>({ request: () => ({ appName: this.$appName(), assetPath: this.$assetPath() }), loader: selectItemGroupsLoader() }) configureResource = ( appName: string, assetPath: string ): ResourceRef<TSelectItemGroup[] | undefined> => { this.$appName.set(appName) this.$assetPath.set(assetPath) return this.#selectItemGroupsResource } // 新增方法获取内部资源 getResource(): ResourceRef<TSelectItemGroup[] | undefined> { return this.#selectItemGroupsResource } }
然后在组件中手动创建实例:
import { Injector, inject } from '@angular/core'; // 注入根注入器 #injector = inject(Injector); $titlesResrc: ResourceRef<TSelectItemGroup[] | undefined> = this.createFacade('registration', 'titles.json').getResource(); $suffixResrc: ResourceRef<TSelectItemGroup[] | undefined> = this.createFacade('registration', 'suffixes.json').getResource(); // 封装实例创建逻辑 private createFacade(appName: string, assetPath: string): SelectItemGroupsResourceFacade { // 创建新注入器并获取服务实例 const facade = this.#injector.create({ providers: [{ provide: SelectItemGroupsResourceFacade }] }).get(SelectItemGroupsResourceFacade); // 配置实例 facade.configureResource(appName, assetPath); return facade; }
方法2:重构服务为构造参数配置
将配置参数移到服务构造函数中,创建实例时直接传入配置,从根源避免后续修改覆盖。
修改服务:
@Injectable({ providedIn: 'root' }) export class SelectItemGroupsResourceFacade { $assetPath = signal<string>(this.assetPath) $appName = signal<string>(this.appName) #selectItemGroupsResource: ResourceRef<TSelectItemGroup[] | undefined> = resource<TSelectItemGroup[], TSelectItemGroupsLoaderRequest>({ request: () => ({ appName: this.$appName(), assetPath: this.$assetPath() }), loader: selectItemGroupsLoader() }) constructor(private appName: string, private assetPath: string) {} getResource(): ResourceRef<TSelectItemGroup[] | undefined> { return this.#selectItemGroupsResource } }
组件中创建实例:
import { Injector, inject } from '@angular/core'; #injector = inject(Injector); $titlesResrc = this.#injector.create({ providers: [ { provide: SelectItemGroupsResourceFacade, useFactory: () => new SelectItemGroupsResourceFacade('registration', 'titles.json') } ] }).get(SelectItemGroupsResourceFacade).getResource(); $suffixResrc = this.#injector.create({ providers: [ { provide: SelectItemGroupsResourceFacade, useFactory: () => new SelectItemGroupsResourceFacade('registration', 'suffixes.json') } ] }).get(SelectItemGroupsResourceFacade).getResource();
方法3:使用注入令牌区分实例
如果需要在组件或其子组件中注入不同实例,可以定义专属注入令牌,在组件providers中配置多个实例。
首先定义注入令牌:
import { InjectionToken } from '@angular/core'; export const TITLES_FACADE = new InjectionToken<SelectItemGroupsResourceFacade>('TITLES_FACADE'); export const SUFFIX_FACADE = new InjectionToken<SelectItemGroupsResourceFacade>('SUFFIX_FACADE');
然后在组件的providers中配置实例:
import { Component, inject } from '@angular/core'; import { TITLES_FACADE, SUFFIX_FACADE } from './your-token-file'; @Component({ // ...其他组件配置 providers: [ { provide: TITLES_FACADE, useFactory: () => { const facade = new SelectItemGroupsResourceFacade(); facade.configureResource('registration', 'titles.json'); return facade; } }, { provide: SUFFIX_FACADE, useFactory: () => { const facade = new SelectItemGroupsResourceFacade(); facade.configureResource('registration', 'suffixes.json'); return facade; } } ] }) export class YourComponent { #titlesFacade = inject(TITLES_FACADE); #suffixFacade = inject(SUFFIX_FACADE); $titlesResrc = this.#titlesFacade.getResource(); $suffixResrc = this.#suffixFacade.getResource(); }
说明
之前直接在组件providers中添加SelectItemGroupsResourceFacade无法得到多个实例,是因为组件内多次inject(SelectItemGroupsResourceFacade)会获取同一个组件级实例,必须通过手动创建注入器或注入令牌的方式区分不同实例。
内容的提问来源于stack exchange,提问作者st_clair_clarke
相关产品推荐
相关产品推荐

