You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:47:07