Angular2 way to update data in nested component from parent component

5.7k views Asked by At

I have two nested components: grid-container and grid-component.

In grid-container, the grid-component is initialized as follows:

<grid [title]="'This is the grid title'" [data]="data"></grid>

Data is initialized. Using events, the updated data in grid-component can be obtained in grid-container. Its obvious until here for me. However, what should we typically do in angular 2 to update data in the nested component (grid-component in this case) from the container component (grid-container)?

More specifically, (or as an illustration) I need a function that adds an item in container component (grid-container) which will be applied in data in grid-component

ps: its not two-way data binding as in this question which also didn't work for this problem.

EDIT:

export class GridComponent{ //child component
  @Input() data:any;
}

import { GridComponent } from 'somewhere';
export class GridContainer{ //parent component
  data:any = [{/*an initialized object which is visible in child component*/}]

  addItem(){
    data.push({/*an item is added*/};
    //PROBLEM IS THE NEWLY ADDED ITEM IS NOT VISIBLE IN GridComponent class.
  }
}
2

There are 2 answers

2
A. Tim On

Your question is not clear. Please provide example of your HTML. If you have Parent-Child relation, then it is 2 way databinding.

@Component({
    template:`
        <grid-component>
            <grid-component [(data)]="gridData"></grid-component>
            <input type="button" label="Update Parent" (click)="updateData()" />
            <label>{{gridData.someCounter}}</label>
        </grid>
    `
})
class GridComponent{
    gridData: any = {
        someCounter = 1;
    };

    updateData() {
        this.gridData.someCounter++;
    }
}

@Component({
    template:`
        <input type="button" label="Update Child" (click)="updateData()" />
        <label>data.someCounter</label>
    `
})
class GridContainerComponent{
    @Input() data: any;
    @Output() dataChange = new EventEmitter();

    //You must trigger this method from GridContainer somehow. E.g. on button click
    updateData() {
        this.data.someCounter++;
        //this row is the most important, and you have to call it manually each time you want to prompt new value to parent. 
        //I.e. in another place you can update 'this.data' but do not update that value in Parent just skipping this line.
        this.dataChange.emit(this.data);
    }
}

If you have 2 Siblings, then it is still 2 way databinding where Parent serves as Proxy:

<grid>
    <grid-component [(dataChild1)]="dataParent"></grid-component>
    <grid-container [(dataChild2)]="dataParent"></grid-container>
</grid>

Or you can use Service Sibling Component Communication

0
Patryk Brejdak On

It's a bit late, but maybe if someone get here will see solution. I think the best solution for this is service which provide easy and clear way to pass data. In your case the child's should subscribe an observable and in parent component you should use service method to pass data whenever you want to update data in child's components. Example below. Also look at the angular 2 cookbook

Parent component

export class GridContainer {
   constructor(private GridService: GridService) {  }
   private data: any[] = [];
   private addItem(el) {
      this.data.push(el);
      this.GridService.pushGridData(this.data);
   }
}

Child Component

export class GridComponent implements OnInit, OnDestroy {
   constructor(private GridService: GridService) {  }
   private data: any[] = [];
   private subscribtion: Subscribtion;
   ngOnInit(): void {
      this.Subscribtion = this.GridService.gridData.subscribe(
         (data) => { this.data = data; }
      )
   }
   ngOnDestroy(): void {
      this.Subscribtion.unsubscribe();
   }
}

Service

@Injectable();
export class GridService {
   constructor() {  }

   private gridDataSource = new Subject<any[]>()

   gridData = this.gridDataSource.asObservable();

   pushGridData(data) {
      this.gridDataSource.next(data);
   }
}