Angular 2 - 在父级初始化之后将数据传递给Child组件

时间:2016-12-15 16:46:01

标签: angular angular-components

我有一个父组件,它通过服务从服务器获取数据。我需要将一些数据传递给Child组件。

我一直在尝试使用通常的@Input()传递此数据,但正如我所预期的那样,我在Child组件中获得的数据是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
    }
}

我已经尝试使用Angular团队的Plunker实时示例进行测试,展示Lifecycle Hooks。在测试中,我更新了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

希望它证明是有用的:)

5 个答案:

答案 0 :(得分:28)

由于数据在开始时未定义,您可以使用* ngIf =&#39;数据&#39;

推迟
$col-padding-xs: 15px;

div {
  padding-right: $col-padding-xs / 2; // returns 7.5px
}

或者您可以在组件上实现ControlValueAccessor并通过ngModel和ngModelChange传递它

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

答案 1 :(得分:15)

您可以使用ngOnChanges获取如下数据

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

<强> OnChanges

  

Angular会在检测到更改时调用其ngOnChanges方法   输入组件(或指令)的属性。

详细了解here

<强>更新

ngOnChanges只会在您更改整个对象时触发,如果您只想更新绑定对象的属性而不是整个对象,则有两个选项,

  1. 直接在模板中绑定属性,请务必使用?

  2. {{childData?.propertyname}}
  3. 或者你可以根据childdata对象创建一个get属性,

    get prop2(){
      return this.childData.prop2;
    }
    
  4. 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

    希望这会有所帮助!!

答案 2 :(得分:3)

当您的ChildComponent(您将其称为ParentComponent,但我认为它是一个错字)被初始化并执行其ngOnInit时,您的父级中没有数据可用,因为您的服务仍然无法获取您的数据。一旦你的数据存在,它将被推送给孩子。

你能做什么取决于你的用例..

如果您只想在ChildComponent 模板中显示数据,则可以使用elvis运算符(?)。例如:{{ childData?}}{{ childData?.myKeyName }} ...

如果您想要做其他事情,可以在ChildComponent中使用 setter 。它将拦截输入变化并让您有机会改变/测试/&#34;做你想做的事情&#34;在你的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的食谱条目。

答案 3 :(得分:1)



   `@Component({
    selector: 'test-child',
    template: `
        
    `
})

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

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

应该是这样吗?我希望这可能是问题所在

`ngOnChanges() { console.log(this.childData); // reference with this

}`

答案 4 :(得分:0)

我假设孩子正在获取父项数据的子集,可能来自用户操作(例如,当用户点击网格行时加载表单)。在这种情况下,我会使用一个事件。

让父母发起一个事件,可能是dataSelected,事件的内容就是所选择的数据。

然后让孩子听取这些事件,并在被解雇时将事件中的数据加载到模型中。

这将有助于解耦您的组件,这总是一个好主意。