I have an Angular2 DI question. Say I have a TestService
and I want to use 2 different instances of this service inside the same component. If I simply add a provider to the component and I add the 2 instances to the constructor I end up with the same service instance. For example:
TestService
import {Injectable} from "@angular/core";
@Injectable()
export class TestService {
public id: number = Math.random();
public toString(): string {
return "Id: " + this.id;
}
}
Test component
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());
}
}
Result in console
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Id: 0.24242492129168425
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB Id: 0.24242492129168425
Can someone pls tell me if there's a way to use Angular2's DI mechanism to inject multiple different instances of a service within the same component or should I just drop the DI for this particular case and create my instances manually with a manual constructor?
Thanks in advance
You can inject a factory that returns a new instance every time you call it:
@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());
}
}
Plunker example
(check the output in the browser console when you click the button)
Given that there is finite amount of instances, the straightforward way may be:
@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
) {}
...
}
Or OpaqueToken
counterpart to avoid overriding services with the same string identifier:
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
) {}
It doesn't hurt DI in TestService
constructor. And keeps the testability of TestComponent
on par, both service instances can be mocked independently.
I would create a static method that returns new instance in the service, and inject it only one through DI in component. Something like:
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();
}
}
then in component:
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());
}
}