2016-12-15 81 views
16

我有一個父組件,它通過服務從服務器獲取數據。我需要將這些數據傳遞給子組件。Angular 2 - 在父初始化後將數據傳遞給子組件

我一直試圖通過這個數據與通常的@Input()然而,因爲我預計我在兒童組件中獲得的數據是undefined

父組件

@Component({ 
    selector: 'test-parent', 
    template: ` 
     <test-child [childData]="data"></test-child> 
    ` 
}) 

export class ParentComponent implements OnInit { 
    data: any; 

    ngOnInit() { 
     this.getData(); 
    } 

    getData() { 
     // calling service and getting data 
     this.testService.getData() 
      .subscribe(res => { 
       this.data = res; 
      }); 
    } 
} 

輔元件

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- Content for child.... --> 
    ` 
}) 

export class ChildComponent implements OnInit { 
    @Input() childData: any; 

    ngOnInit() { 
     console.log(childData); // logs undefined 
    } 
} 

我保持簡單,以顯示我想要的例子做。我的問題是初始化之後是否有可能將數據傳遞給孩子。我不確定,但在這種情況下雙向數據綁定會有幫助嗎?

更多的調查

繼公佈答案,我已經使用ngOnChanges生命週期掛鉤嘗試。我遇到的問題是當數據更新時ngOnChanges函數未被觸發。數據是一個對象,這就是爲什麼我認爲變化沒有被檢測到。在子組件

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- Content for child.... --> 
    ` 
}) 

export class ChildComponent implements OnChanges { 
    @Input() childData: any; 

    ngOnChanges() { 
     console.log(childData); // logs undefined 
    } 
} 

更新後的代碼我試着從團隊角度展示了生命週期掛鉤Plunker的活生生的實例進行測試。在測試中,我更新了OnChangesComponent來傳遞一個對象,正如在更新對象時可以看到的,ngOnChanges沒有檢測到這個變化。 (這也是在這個question所示)

https://plnkr.co/edit/KkqwpsYKXmRAx5DK4cnV

似乎ngDoCheck可以幫助解決這個問題,但在我看來,作爲每一個變化是由該檢測它不會幫助,從長遠來看錶現生命週期鉤子。

而且我知道,我大概可以使用@ViewChild()訪問子組件,並設置我需要的變量,但我不認爲這個用例它使多大意義。

發佈更多的代碼,以幫助更好地解釋

父組件

@Component({ 
    selector: 'test-parent', 
    template: ` 
     <test-child [childType]="numbers" [childData]="data" (pickItem)="pickNumber($event)"> 
      <template let-number> 
       <span class="number" [class.is-picked]="number.isPicked"> 
        {{ number.number }} 
       </span> 
      </template> 
     </test-child> 
     <test-child [childType]="letters" [childData]="data" (pickItem)="pickLetter($event)"> 
      <template let-letter> 
       <span class="letter" [class.is-picked]="letter.isPicked"> 
        {{ letter.displayName }} 
       </span> 
      </template> 
     </test-child> 
     <button (click)="submitSelections()">Submit Selection</button> 
    ` 
}) 

export class ParentComponent implements OnInit { 
    data: any; 
    numbersData: any; 
    lettersData: any; 

    ngOnInit() { 
     this.getData(); 
    } 

    getData() { 
     // calling service and getting data for the game selections 
     this.testService.getData() 
      .subscribe(res => { 
       this.data = res; 
       setChildData(this.data); 
      }); 
    } 

    setChildData(data: any) { 
     for(let i = 0; i < data.sections.length; i++) { 
      if(data.sections[i].type === 'numbers') { 
       this.numbersData = data.sections[i]; 
      } 

      if(data.sections[i].type === 'letters') { 
       this.lettersData = data.sections[i]; 
      } 
     } 
    } 

    pickNumber(pickedNumbers: any[]) { 
     this.pickedNumbers = pickedNumbers; 
    } 

    pickLetter(pickedLetters: any[]) { 
     this.pickedLetters = pickedLetters; 
    } 

    submitSelections() { 
     // call service to submit selections 
    } 
} 

輔元件

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- 
      Content for child... 
      Showing list of items for selection, on click item is selected 
     --> 
    ` 
}) 

export class ChildComponent implements OnInit { 
    @Input() childData: any; 
    @Output() onSelection = new EventEmitter<Selection>; 

    pickedItems: any[]; 

    // used for click event 
    pickItem(item: any) { 
     this.pickedItems.push(item.number); 

     this.onSelection.emit(this.pickedItems); 
    } 
} 

這或多或少是我的代碼。基本上我有一個父處理子組件的選擇,然後我將這些選擇提交給服務。我需要將某些數據傳遞給孩子,因爲我試圖讓孩子以服務期望的格式返回對象。這將幫助我不要在父組件中創建服務期望的整個對象。此外,它會讓孩子重複使用,因爲它會發回服務期望的內容。

更新

我明白,用戶仍然張貼在這個問題的答案。請注意,上面發佈的代碼適用於我。我遇到的問題是,在一個特定的模板中,我輸入了一個導致數據爲undefined的錯字。

希望,它的證明是有用的:)

回答

7

可以使用ngOnChanges得到類似下面的數據,

ngOnChanges() { 
    if(!!childData){   
     console.log(childData);   
    } 
} 

OnChanges

角調用其ngOnChanges方法時,它檢測組件(或指令)輸入屬性的更改。

瞭解更多關於它here

更新

ngOnChanges只會如果更改整個對象,如果你想只更新綁定的對象不是整個對象,你有兩個選擇的屬性火,

  1. 要麼綁定財產直接在模板中,請確保使用?類似{{childData?.propertyname}}

  2. 或者您可以創建一個get屬性取決於childdata對象,

    get prop2(){ 
        return this.childData.prop2; 
    } 
    

Copmlete實施例,

import { Component, Input } from '@angular/core'; 

@Component({ 
    selector: 'my-app', 
    template: `<h1>Hello {{name}}</h1> 
    <child-component [childData]='childData' ></child-component> 
    ` 
}) 
export class AppComponent { 
    name = 'Angular'; 
    childData = {}; 
    constructor(){ 
    // set childdata after 2 second 
    setTimeout(() => { 
     this.childData = { 
     prop1 : 'value of prop1', 
     prop2 : 'value of prop2' 
     } 
    }, 2000) 
    } 
} 

@Component({ 
    selector: 'child-component', 
    template: ` 
    prop1 : {{childData?.prop1}} 
    <br /> 
    prop2 : {{prop2}} 
    ` 
}) 
export class ChildComponent { 
    @Input() childData: {prop1: string, prop2: string}; 

    get prop2(){ 
    return this.childData.prop2; 
    } 
} 

這裏是Plunker

希望這有助於!

+0

傳遞感謝您的回答,這似乎指向了我正確的方向。現在唯一的問題是,如果數據是一個「簡單」字符串,那麼'ngOnChanges'正在檢測變化,但是如果它是一個對象,出於某種原因它將保持'undefined'。 –

+0

@DanielGrima:更新了答案。 –

+0

再次感謝您的幫助......我剛剛發現問題所在。在我的模板變量中有一個錯字,所以當然總是未定義。之前沒有意識到!至少它看起來像是在工作,顯然我不需要任何'ngOnChanges'或getter對象..現在看起來沒問題。 –

0

我假設孩子正在獲取父母數據的一個子集,可能來自用戶操作(例如用戶點擊網格行時加載表單)。在這種情況下,我會使用一個事件。

讓父項觸發一個事件,可能是dataSelected,事件的內容將是選定的數據。

然後讓孩子監聽這些事件,並在被觸發時將事件中的數據加載到模型中。

這將幫助解耦您的組件,這總是一個好主意。

0

當你的ChildComponent(你也稱它爲ParentComponent,但我猜它是一個錯字)得到初始化並執行它的ngOnInit時,父母中沒有可用的數據,因爲你的服務仍然不能獲取你的數據。一旦你的數據在那裏,它會被推送給孩子。

你可以做什麼取決於你的用例..

如果你只是想在你的ChildComponent 模板顯示的數據,你可以使用Elvis操作符(?)。是這樣的:{{ childData?}}{{ childData?.myKeyName }} ......

如果你想要做一些其他的東西,你可以在你的ChildComponent使用二傳手。它會攔截輸入變化,讓您有機會改變/測試/「你什麼都想要」用它在你ChildComponent做其他事情之前..如:

@Component({ 
    selector: 'test-child', 
    template: ` 
     <!-- Content for child.... --> 
    ` 
}) 

export class ChildComponent implements OnInit { 
    private _childData: any; 
    @Input() 
    set childData(parentData: any) { 
     // every time the data from the parent changes this will run 
     console.log(parnetData); 
     this._childData = parentData; // ...or do some other crazy stuff 
    } 
    get childData(): any { return this._childData; } 

    ngOnInit() { 
     // We don't need it for that... 
    } 
} 

或使用作爲Madhu提到的ngOnChanges

您也可以在官方文檔中查看Component Communication的食譜條目。

+0

是的,這是你提到的一個錯字 - 我已經用更多的幾點更新了這個問題。我也嘗試了你的建議,但是我仍然得到相同的結果:/ –

+0

你介意發佈你的原始代碼嗎? 我不是100%清楚它爲什麼不適合你... 你可能想要嘗試的另一件事是 - 給你在你的ParentComponent中用你的服務中的數據做其他事情 - 你的'testService.getData()'直接放到你的子輸入中,讓角度處理'async'管道的訂閱。就像這樣:' ' –

+0

我已經用更多的代碼更新了這個問題..希望它能讓它更清晰......感謝您的幫助! –

17

因爲數據是在開始不確定,你可以推遲它* ngIf =「數據」

<div *ngIf='data'> 
    <test-child [childData]="data"></test-child> 
</div> 

或者您可以在組件上實現ControlValueAccessor和ngModel與ngModelChange

<test-child [ngModel]="data?" (ngModelChange)="data? ? data= $event : null"></test-child> 
相關問題