【问题标题】:dependency injection creating multiple instances依赖注入创建多个实例
【发布时间】:2017-07-19 19:30:26
【问题描述】:

我有一个组件用于向我的应用程序显示一些常见的输出。该组件被注入到其他服务中,以便这些服务可以触发该行为。经过大量故障排除后,我想我已经将问题跟踪到 Angular 的 DI 创建组件的多个实例。我创建了一个简单的版本来说明这个问题。这是 Angular 2.4.x

应用模块:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';

import {AppComponent} from './app.component';
import {TestComponent} from "./test.component";

@NgModule({
    declarations: [
        AppComponent,
        TestComponent
    ],
    imports: [BrowserModule, FormsModule, HttpModule],
    providers: [TestComponent],
    bootstrap: [AppComponent]
})
export class AppModule {
}

测试组件(这是我试图用来显示信息并将其用作服务的组件的简化版本):

import {Component, OnInit} from '@angular/core';
@Component({
    selector: 'app-test',
    template: `<p>Message:</p>
    <p *ngIf="show">hi</p>`
})
export class TestComponent {
    private show: boolean = false;
    doTest() {
        setTimeout(() => {
            console.log('timeout callback');
            this.show = true;
        }, 5000);
    }
}

App 组件(使用我的测试组件的组件):

import {Component, OnInit} from '@angular/core';
import {TestComponent} from "./test.component";

@Component({
    selector: 'app-root',
    template:`
<h1>{{title}}</h1>
<app-test></app-test>
`
})
export class AppComponent implements OnInit{
    title = 'app works!';
    constructor(private test: TestComponent) { }
    ngOnInit() {
        this.test.doTest();
    }
}

我希望的行为是 AppComponent 会调用 TestComponent 的 doTest 函数,并且 TestComponent 会在 5 秒后显示“hi”消息。

回调发生,我看到控制台消息,但没有显示“hi”。我认为依赖注入提供了单独的实例,因此注入 App 的构造函数的实例与 App 模板中的实例不同。

如果我的理解是正确的,我该如何让它在两种情况下都是相同的?我是否错过了实现这种行为的更好方法?

【问题讨论】:

  • 要获得对测试组件的引用,您可以在AppComponent 的模板中使用&lt;app-test #test&gt; 或使用ViewChild 装饰器。将其注入构造函数是不正确的,但考虑这样做是合理的。
  • TestComponent 或任何其他组件不应在 provider 属性中声明为提供程序。通常,您只会放置用@Injectable() 修饰的类,通常是服务。
  • 我本来打算有一个单独的服务,但我在某处(我认为是另一个堆栈溢出帖子)读到 Component 扩展了 Injectable,因此任何标记为组件的东西都可以被视为可注入的(无论是否这是明智的做法...)
  • @AlexanderStaroselsky 您绝对正确,不应将组件添加到 providers 数组中,但它与 Injectable 装饰器工厂无关。
  • @AluanHaddad 你是绝对正确的。我同意您利用ViewChild 解决此问题的方法。我发表评论的目的是促进关于providers 的更好实践。谢谢!

标签: angular


【解决方案1】:

您需要使用ViewChild 装饰器来访问子组件,而不是将其注入到您的构造函数中。直观地说,您尝试做的事情是有道理的,但它不会起作用。

请注意,正如 Alexander Staroselsky 在他的评论中指出的那样,您不应在 providers 数组中列出任何组件!

这里是你需要写的

import { Component, NgModule, ViewChild } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

@Component({
    selector: 'app-test',
    template: `
        <p>Message:</p>
        <p *ngIf="show">hi</p>
    `
})
export class AppTestComponent {
    doTest() {
        setTimeout(() => {
            console.log('timeout callback');
            this.show = true;
        }, 3000);
    }
    show = false;
}

@Component({
    selector: 'my-app',
    template: '<app-test></app-test>',
})
export class App {
    // this queries your view for elements of the type passed to the
    // ViewChild decorator factory.
    @ViewChild(AppTestComponent) test: AppTestComponent;

    ngAfterContentInit() {
        this.test.doTest();
    }
}

@NgModule({
    imports: [BrowserModule],
    declarations: [App, AppTestComponent],
    bootstrap: [App]
})
export class AppModule { }

这是一个工作示例

https://plnkr.co/edit/DDx4INrgixsnJKiU1E0h?p=preview

如果您在视图中仅使用子组件,则可以省略所有额外的装饰器和生命周期钩子。

例如:

@Component({
    selector: 'my-app',
    template: `
        <!-- give the child component a name for use in the template -->
        <app-test #test>
        </app-test>
        <button (click)="test.doTest()">Do Test</button>
    `,
})
export class App {}

这是一个工作示例

https://plnkr.co/edit/nnXW2z7pbgdTk5Qzl1Sw?p=preview

【讨论】:

  • 谢谢。我认为这解决了我上面说明的问题,但我认为在尝试创建我的问题的简单版本(而不是发布实际代码 sn-ps)时,我稍微歪曲了我的问题。组件 A 有我的 TestComponent,但它是第三个服务(称为服务 B),它实际上调用了 TestComponent 的 doTest 方法。我想我可能应该这样做:stackoverflow.com/a/36404625/3621712 创建一个服务并让我的测试组件监听来自服务的事件。然后其他服务可以调用将触发新事件的方法。
  • 也许可以详细说明问题或提出新问题。
  • 只是出于好奇,中间人还有哪些其他选择?
  • 您可以在您的应用程序中使用由某些 ECMAScript 模块导出的普通旧共享对象或函数(好的,但不理想)。你可以使用一个全局变量(很糟糕),你可以使用localStorage 和它的原生事件(如果你想缓存的话很有趣)。也就是说,服务是您最好的选择,但我想指出“Angular 方式”并不是唯一的方式。
  • 谢谢。来自 Java(以及该保护伞下的所有各种选项)背景最近做 Angular 是一次有趣的经历。永远感谢了解所有可用的选项。
猜你喜欢
  • 1970-01-01
  • 2016-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-02
  • 1970-01-01
相关资源
最近更新 更多