2017-07-19 170 views
0

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

的AppModule:

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

应用程序组件(使用我的测试色差分量):

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'消息。

回调发生,我看到控制台消息,但'嗨'不显示。我认为这对于提供单独实例的依赖注入来说很重要,因此注入App的构造函数的实例与App的模板中的实例不同。

如果我的理解是正确的,我该如何使它在两种情况下都是相同的实例?我错过了一个更好的方式去实现这种行为?

+0

要得到的测试组件,您可以用'<应用程序测试#TEST>'引用您的' AppComponent'的模板或者使用'ViewChild'装饰器。将它注入到构造函数中是不正确的,但认为这样做是合理的。 –

+1

'TestComponent'或任何其他组件不应该在'provider'属性中声明为提供者。一般来说,你只会把用@Injectable()装饰的类,通常是服务。 –

+0

我原本打算有一个单独的服务,但我读了某处(我认为另一个堆栈溢出帖子),该组件扩展了Injectable,因此标记为组件的任何东西都可以看作是可注入的(无论这是否明智。 ..) –

回答

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

+1

谢谢。我认为这解决了我上面说明的问题,但我认为在试图创建我的问题的简单版本(而不是张贴实际的代码片段)时,我稍微错误地描述了我的问题。 组件A有我的TestComponent,但是它是第三个服务(称为服务B),它实际上调用了TestComponent的doTest方法。 我想我可能应该这样做:https://stackoverflow.com/a/36404625/3621712创建一个服务,并让我的测试组件监听来自服务的事件。然后其他服务可以调用将触发新事件的方法。 –

+0

也许详细说明问题或问一个新问题。 –

+0

@josh_in_dc根据您更新的评论,我同意在相关答案中建议的方法。有很多方法可以达到这个目标,但你绝对需要一个中介。服务是合乎逻辑的选择,但也有其他方法。 –