Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 - Passing data to Child component after Parent initialisation

I have a parent component which is getting data from a server through a service. I need some of this data to be passed to the Child component.

I've been trying to pass this data with the usual @Input() however as I expected the data I'm getting in the Child component is undefined.

Example

Parent Component

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

Child Component

@Component({     selector: 'test-child',     template: `         <!-- Content for child.... -->     ` })  export class ChildComponent implements OnInit {     @Input() childData: any;      ngOnInit() {         console.log(childData); // logs undefined     } } 

I've kept the example simple so as to show what I'm trying to do. My question is whether it's possible to pass data to the child after the initialisation. I'm not sure but would two-way data binding help in this case?

More Investigation

Following the posted answers I've tried using the ngOnChanges lifecycle hook. The issue I'm having is that the ngOnChanges function is not being fired when the data is updated. The data is an object which is why I think the changes are not being detected.

Updated code in the Child Component

@Component({     selector: 'test-child',     template: `         <!-- Content for child.... -->     ` })  export class ChildComponent implements OnChanges {     @Input() childData: any;      ngOnChanges() {         console.log(childData); // logs undefined     } } 

I've tried a test with the Live Examples on Plunker from the Angular team showcasing the Lifecycle Hooks. In the test I've updated the OnChangesComponent to pass an object which as can be seen in the example when updating an object the ngOnChanges is not detecting the change. (This is also shown in this question)

https://plnkr.co/edit/KkqwpsYKXmRAx5DK4cnV

Seems that the ngDoCheck could help solve this issue, but it seems to me that it would not help performance in the long run as every change is detected by this Lifecycle Hook.

Also I know that I could probably use the @ViewChild() to access the Child Component and set the variables that I need, but I don't think that for this use-case it makes much sense.

Posting more code to help explain better

Parent Component

@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     } } 

Child Component

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

That's more or less the code that I have. Basically I have a parent that handles selections from child components and then I submit these selections to the service. I need to pass certain data to the child because I'm trying to have the object that the child returns in the format that the service is expecting. This would help me not to create the whole object expected by the service in the parent component. Also it would make the child reuseable in the sense that it sends back what the service expects.

Update

I appreciate that users are still posting answers on this question. Note that the code that has been posted above worked for me. The issue I had was that in a particular template I had a typo which caused for the data to be undefined.

Hope that it's proving to be helpful :)

like image 849
Daniel Grima Avatar asked Dec 15 '16 16:12

Daniel Grima


People also ask

How will you share the data from parent to child component in Angular?

To let Angular know that a property in a child component or directive can receive its value from its parent component we must use the @Input() decorator in the said child. The @Input() decorator allows data to be input into the child component from a parent component.


1 Answers

Since data is undefined at start, you can postpone it with *ngIf='data'

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

Or you can implement ControlValueAccessor on your component and pass it by ngModel with ngModelChange

<test-child [ngModel]="data?" (ngModelChange)="data? ? data= $event : null"></test-child> 
like image 138
Mopa Avatar answered Oct 11 '22 09:10

Mopa