Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Passing data from Parent component to child

Tags:

I am passing products from a parent component to a child component using input binding and it works the first time the child component is invoked. I expect change to products in filterProduct() to be reflected in the child every time it is modified in the parent but this is not happening. How do I get this to happen.

Parent template:

<div class="container-fluid">
<div class="col-sm-9">
    <app-product-card [products]=products></app-product-card>
</div>

Parent component:

@Component({
  selector: 'app-appliances-product-card',
  moduleId: module.id,
  templateUrl: 'appliances-product-card.component.html'
})

export class AppliancesProductCardComponent implements OnInit{    
   products: Product[];

   filterProduct(filter) {
     this.products = this.filteredProducts;         
  }
}

Child component:

@Component({
  selector: 'app-product-card',
  moduleId: module.id,
  templateUrl: 'product-card.component.html'
})

export class ProductCardComponent {
  @Input() products: Product[];
}
like image 339
koque Avatar asked Jun 27 '17 14:06

koque


People also ask

How do you pass data from parent component 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.

Can we pass function from parent to child in Angular?

@Input Decorator: This is used to define an input property and it is used to send data from parent component to child component. @Output Decorator: This is used to bind a property of the type of Angular EventEmitter class and it is used to pass data from child component to parent component.

How do you share data between parent and child components?

@Input() and @Output() give a child component a way to communicate with its parent component. @Input() lets a parent component update data in the child component. Conversely, @Output() lets the child send data to a parent component.

How do you pass data from parent to child component in Angular on button click?

Pass data from parent to child component using @Input() decorator, which allows data to pass through templates and child to parent component using @Output() decorator with the help of Event Emitter. Create a new Angular project using the following NPM command: ng new componentSharing.


2 Answers

Here's plunker of what might work for you:

https://plnkr.co/edit/S47fBh2xdT1gp2zrznk4?p=preview

Instead of passing the data from parent to child component, both parent and child component will use service to get the data.

Parent Component:

import { Component, OnInit } from '@angular/core';
import { Product } from './product'    
import { ProductsService } from './products.service';

@Component({
  selector: 'app-appliances-product-card',
  template: `
    <div class="container-fluid">
      <button (click)="filterProduct('Type 1')">Filter Type 1</button>
      <button (click)="filterProduct('')">Clear Filter</button>
      <div class="col-sm-9">
        <app-products-card></app-products-card>
      </div>
    </div>
  `
})
export class AppliancesProductCardComponent implements OnInit {
  products: Product[];      
  constructor(private _productsService: ProductsService){ }      

  filterProduct(type: string) {
    this.products = this._productsService.filterProduct(type);
  }      

  ngOnInit() { 
    this.products = this._productsService.getAllProducts();
  }
}

Child Component:

import { Component, OnInit, Input } from '@angular/core';    
import { Product } from './product';    
import { ProductsService } from './products.service';

@Component({
  selector: 'app-products-card',
  template: `
    <h1>Product Card</h1>
      <div *ngFor="let product of products">{{product.name}} - {{product.type}}</div>
  `
})
export class ProductsCardComponent implements OnInit {      
  constructor(private _productsService: ProductsService){ }

  ngOnInit() { 
    this.products = this._productsService.getAllProducts();        
    this._productsService.filteredProducts.subscribe(products => {
      console.log(products);
      this.products = products
    });
  }
}

Service:

import { EventEmitter, Injectable } from '@angular/core';    
import { Product } from './product';

export class ProductsService {
  filteredProducts = new EventEmitter<Product[]>;

  private products: Product[] = [
      { id: 1, name: 'Product 1', price: 10.50, type: 'Type 1' },
      { id: 2, name: 'Product 2', price: 15.50, type: 'Type 1' },
      { id: 3, name: 'Product 3', price: 1.50, type: 'Type 2' },
      { id: 4, name: 'Product 4', price: 100.50, type: 'Type 3' }
    ];

  getAllProducts(): Product[] {
    return this.products.slice();
  }

  filterProduct(type: string): Product[]{
    let filteredProducts = this.products.filter(p => !type || p.type == type).slice();
    this.filteredProducts.emit(filteredProducts);
  }
}

I came across similar problems when I first started writing angular application. Passing data from parent to child component all the time is not a manageable way to write application. Specially if it's a big application with too many nested components.

In this example, I'm using the services and event emitter (both part of angular framework) to provide relevant data to both the child and the parent component.

One way to improve the code is to use rxjs. This is what angular event emitter uses behind the scene.

And even more advanced solution is to use ngrx/store (redux library for Angular). This is particularly suited for huge applications.

No matter which solution you choose, the idea is to have a single place to maintain the state of the application and avoid nesting data too much.

like image 181
Suyogya Shrestha Avatar answered Oct 11 '22 02:10

Suyogya Shrestha


If you'd like for the child component to react to changes in the parent component you'll need to implement ngOnChanges() with SimpleChange from @angular/core. Detailed documentation can be found on the Angular - linkComponent Interaction Page, but the main idea is expressed in two chunks of code:

Child Component

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

@Component({
  selector: 'version-child',
  template: `
    <h3>Version {{major}}.{{minor}}</h3>
    <h4>Change log:</h4>
    <ul>
      <li *ngFor="let change of changeLog">{{change}}</li>
    </ul>
  `
})
export class VersionChildComponent implements OnChanges {
  @Input() major: number;
  @Input() minor: number;
  changeLog: string[] = [];

  ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
    let log: string[] = [];
    for (let propName in changes) {
      let changedProp = changes[propName];
      let to = JSON.stringify(changedProp.currentValue);
      if (changedProp.isFirstChange()) {
        log.push(`Initial value of ${propName} set to ${to}`);
      } else {
        let from = JSON.stringify(changedProp.previousValue);
        log.push(`${propName} changed from ${from} to ${to}`);
      }
    }
    this.changeLog.push(log.join(', '));
  }
}

Parent Component

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

@Component({
  selector: 'version-parent',
  template: `
    <h2>Source code version</h2>
    <button (click)="newMinor()">New minor version</button>
    <button (click)="newMajor()">New major version</button>
    <version-child [major]="major" [minor]="minor"></version-child>
  `
})
export class VersionParentComponent {
  major = 1;
  minor = 23;

  newMinor() {
    this.minor++;
  }

  newMajor() {
    this.major++;
    this.minor = 0;
  }
}
like image 45
Z. Bagley Avatar answered Oct 11 '22 01:10

Z. Bagley