2017-07-19 199 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根據您更新的評論,我同意在相關答案中建議的方法。有很多方法可以達到這個目標,但你絕對需要一箇中介。服務是合乎邏輯的選擇,但也有其他方法。 –