【问题标题】:Angular2 DI - initializing multiple different instances in the same constructorAngular2 DI - 在同一个构造函数中初始化多个不同的实例
【发布时间】:2016-11-07 21:56:22
【问题描述】:

我有一个 Angular2 DI 问题。假设我有一个TestService,并且我想在同一个组件中使用此服务的 2 个不同实例。如果我只是向组件添加一个提供程序,然后将 2 个实例添加到构造函数,我最终会得到相同的服务实例。例如:

测试服务

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

@Injectable()
export class TestService {

    public id: number = Math.random();

    public toString(): string {
        return "Id: " + this.id;
    }
}

测试组件

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());
    }
}

控制台中的结果

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Id: 0.24242492129168425
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB Id: 0.24242492129168425

谁能告诉我是否有办法使用 Angular2 的 DI 机制在同一个组件中注入多个不同的服务实例,或者我应该为这种特殊情况删除 DI 并使用手动构造函数手动创建我的实例?

提前致谢

【问题讨论】:

    标签: dependency-injection typescript angular


    【解决方案1】:

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

    @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 不会受到伤害。并保持TestComponent 的可测试性相同,两个服务实例都可以独立模拟。

    【讨论】:

    【解决方案2】:

    你可以注入一个每次调用它都会返回一个新实例的工厂:

    @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 (单击按钮时检查浏览器控制台中的输出)

    【讨论】:

    • 这根本就是不工作。如果使用 useFactory 注入,工厂将仅用于注入 _testServiceFactory 一次。但是这个 sn-p 无论如何都不会工作,因为你没有构造函数参数的类型,angular 不知道要注入什么。
    • @TimKachko 非常感谢您的宝贵反馈!我更新了我的答案。你是对的,关于类型。我将其更改为使用字符串键而不是@Inject()。不过工厂会工作。也许我可以在代码中让它更明显,但是工厂是一个返回函数的函数,因此 DI 将保留返回函数的一个实例并将其传递给每个注入 'testService' 的类,并且因为传递了一个函数对于每次调用时返回 new TestService() 的构造函数,这将起作用。
    • 让您尝试在 Plucker 中使用它,您会明白我的意思。 useFactory 将注入 TestService,而不是可用于创建 TestService 的工厂方法。
    • Plunker 在我的 Chrome 版本中不能很好地工作一段时间 :-/。看到useFactory: (...) => (...) => new TestService()(double () =>) 工厂返回的值是工厂函数了吗?
    • 它只有一个 ()=>,因此返回一个类实例,而我的示例 (double ()=>) 返回一个函数
    【解决方案3】:

    我将创建一个在服务中返回新实例的静态方法,并通过组件中的 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());
        }
    }
    

    【讨论】:

    • 这也符合 Angular 2 团队文档中的编码风格。
    • 问题是你将这两个实例存储在哪里以防其他组件​​需要它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 2015-04-28
    • 2014-08-25
    • 1970-01-01
    • 2015-12-04
    相关资源
    最近更新 更多