Dependency injection Angular2 DI-在同一构造函数中初始化多个不同实例

Dependency injection Angular2 DI-在同一构造函数中初始化多个不同实例,dependency-injection,typescript,angular,Dependency Injection,Typescript,Angular,我有一个问题。假设我有一个TestService,我想在同一个组件中使用该服务的两个不同实例。如果我简单地向组件添加一个提供者,并将这两个实例添加到构造函数中,那么最终将得到相同的服务实例。例如: 测试服务 import {Injectable} from "@angular/core"; @Injectable() export class TestService { public id: number = Math.random(); public toString()

我有一个问题。假设我有一个
TestService
,我想在同一个组件中使用该服务的两个不同实例。如果我简单地向组件添加一个提供者,并将这两个实例添加到构造函数中,那么最终将得到相同的服务实例。例如:

测试服务

import {Injectable} from "@angular/core";

@Injectable()
export class TestService {

    public id: number = Math.random();

    public toString(): string {
        return "Id: " + this.id;
    }
}
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Id: 0.24242492129168425
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB Id: 0.24242492129168425
测试组件

import {Component, Input, OnInit} from "@angular/core";
import {TestService} from "../../services/test.service";

@Component({
    providers: [TestService]
})
export class TestComponent implements OnInit {

    constructor(private _testService1: TestService, private _testService2: TestService) { };

    ngOnInit() {
        console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", this._testService1.toString());
        console.log("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", this._testService2.toString());
    }
}
在控制台中生成结果

import {Injectable} from "@angular/core";

@Injectable()
export class TestService {

    public id: number = Math.random();

    public toString(): string {
        return "Id: " + this.id;
    }
}
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Id: 0.24242492129168425
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB Id: 0.24242492129168425
有人能告诉我,是否有一种方法可以使用Angular2的DI机制在同一个组件中注入多个不同的服务实例,或者我应该为这种特殊情况放弃DI,并使用手动构造函数手动创建实例


提前感谢

我将创建一个静态方法,在服务中返回新实例,并通过组件中的DI只注入一个实例。比如:

import {Injectable} from "@angular/core";

@Injectable()
export class TestService {

    public id: number = Math.random();

    public toString(): string {
        return "Id: " + this.id;
    }
    static init() {
      return new TestService();
    }
}
然后在组件中:

import {Component, Input, OnInit} from "@angular/core";
import {TestService} from "../../services/test.service";

@Component({
    providers: [TestService]
})
export class TestComponent implements OnInit {
    _testService1: TestService;
    _testService2: TestService;

    constructor(_testFactory: TestService) { 
       this._testService1 = _testFactory.init();
       this._testService2 = _testFactory.init();
    };

    ngOnInit() {
        console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", this._testService1.toString());
        console.log("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", this._testService2.toString());
    }
}

鉴于实例数量有限,简单的方法可能是:

@Component({
    providers: [
        { provide: 'TestService1', useClass: TestService },
        { provide: 'TestService2', useClass: TestService }
    ]
})
export class TestComponent implements OnInit {
    constructor(
        @Inject('TestService1') private _testService1: TestService,
        @Inject('TestService2') private _testService2: TestService
    ) {}
    ...
}
OpaqueToken
对应项,以避免覆盖具有相同字符串标识符的服务:

export const TestService1 = new OpaqueToken;
export const TestService2 = new OpaqueToken;

...
providers: [
    { provide: TestService1, useClass: TestService },
    { provide: TestService2, useClass: TestService }
]
...
constructor(
    @Inject(TestService1) private _testService1: TestService,
    @Inject(TestService2) private _testService2: TestService
) {}

它不会伤害
TestService
构造函数中的DI。同时保持“代码>测试组件在PAR上的可测试性,两个服务实例都可以独立地被嘲笑。

< P>您可以在每次调用它时注入一个返回新实例的工厂:

@NgModule({
   providers: [{
      provide: 'testService', 
      useFactory: (/* TestService deps here like `http`*/) => 
        (/* params */) => new TestService(/* http */), 
      deps: [/* TestService deps here like `Http`*/ ]
    }]
})


@Component(...)
export class TestComponent implements OnInit {

    constructor(@Inject('testService') private _testServiceFactory) { };

    ngOnInit() {
        console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", this._testServiceFactory( /* params */).toString());
        console.log("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", this._testServiceFactory().toString());
    }
}

(单击按钮时检查浏览器控制台中的输出)

这也符合Angular 2团队文档中的编码样式。问题是,如果其他组件需要,您将这两个实例存储在何处。答案很好。最后,我想说,为了避免命名冲突,还应该使用OpaqueToken,而不是字符串:@CharlesHETIER谢谢,这里使用字符串标识符是为了简洁,但提到OpaqueToken也可以使用也没什么坏处。它根本不起作用。如果使用useFactory进行注入,则该工厂将仅用于注入_testServiceFactory一次。但是这个代码段无论如何都不会工作,因为您没有构造函数参数的类型,angular不知道注入什么。@TimKachko非常感谢您的宝贵反馈!我更新了我的答案。关于类型,你是对的。我用
@Inject()
将其改为使用字符串键。不过,这家工厂会运作的。也许我可以在代码中更清楚地说明这一点,但工厂是一个返回函数的函数,因此DI将保留一个返回函数的实例,并将其传递给注入
'testService'
的每个类,因为函数被传递给返回
新testService()的构造函数
每次调用它时,它都会起作用。让您尝试在Pucker中使用它,您就会明白我的意思。useFactory将注入TestService,而不是一个可用于创建TestService的工厂方法。Plunker在我的Chrome版本中不久就无法正常工作:-/。您是否看到了
useFactory:(…)=>(…)=>newtestservice()
(double
()=>
)其中工厂返回的值是一个工厂函数?它只有一个
()=>
,因此返回一个类实例,而我的示例(double
()=>
)返回一个函数