Home > Software design >  Is there a way to pass an object using emit to the child component? (Angular)
Is there a way to pass an object using emit to the child component? (Angular)

Time:06-16

I've created a button whenever I click on it, it passes all values to this empty object 'product'. I'm trying to emit this object to the child component, so I can push it inside of the product empty array.

    import { Component, Input } from '@angular/core';
    import { Item } from 'src/app/Model/Item';
    import { EventEmitter } from '@angular/core';
    @Component({
      selector: 'app-item',
      templateUrl: './item.component.html',
      styleUrls: ['./item.component.css']
    })
    export class ItemComponent  {
      items: Item[] = []
      @Input() addProduct: EventEmitter<Item> = new EventEmitter
      constructor(private storeService: StoreService) {
        this.items = this.storeService.getItem()
      }
addProducts(item: Item) {
    const product = {
      id: item.id,
      img: item.img,
      name: item.name,
      type: item.type,
      price: item.price,
      available: item.available
    }
    console.log(product)
    this.addProduct.emit(product)
    }
    }

-- (template of the component)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <section>
        <div >
        <ul *ngFor="let item of items">
                <img [routerLink]="['/product', item.id]" src = {{item.img}} alt="store pictures"/>
                <li >{{item.name}}</li>
                <li>{{item.type}}</li>
                <li>{{item.available}}</li>
                <li>{{item.price}}</li>
                <button (click)="addProducts(item)">Add to Cart</button>
            </ul>
        </div>
    </section>
</body>
</html>

--

// another component (child)

import { Component, OnInit } from '@angular/core';
import { Item } from 'src/app/Model/Item';

@Component({
  selector: 'app-cart',
  templateUrl: './cart.component.html',
  styleUrls: ['./cart.component.css']
})
export class CartComponent implements OnInit {
  product: Item[] = []
  ngOnInit(): void {
  }

addProduct(post: Item){
this.product.push(post)
}
}

-- Here is what I did in the child template.

<app-cart (addProduct)="addProduct($event)"></app-cart>

It give me an error saying, "Argument of type 'Event' is not assignable to parameter of type 'Item'. Type 'Event' is missing the following properties from type 'Item': id, img, name, price, availablengtsc(2345)"

CodePudding user response:

If you want to understand how things work in Angular, I suggest following the Angular tutorial.

But here's a simple example of how you can communicate between components.

Items List Component

@Component({
  selector: 'app-items-list',
  templateUrl: './app-items-list.component.html',
  styleUrls: ['./app-items-list.component.css']
})
export class ItemsListComponent {
  @Input() items: Item[] = [];
  @Output() addProduct = new EventEmitter<Item>();

  constructor(private storeService: StoreService) {
    this.items = this.storeService.getItem();
  }

  addProducts(item: Item) {
    this.addProduct.emit(item);
  }
}

You don't need to add the initial HTML markup outside of the main HTML file which is index.html

<div >
  <ul *ngFor="let item of items">
    <img [routerLink]="['/product', item.id]" [src]="item.img" alt="store pictures" />
    <li>{{ item.name }}</li>
    <li>{{ item.type }}</li>
    <li>{{ item.available }}</li>
    <li>{{ item.price }}</li>
    <button (click)="addProducts(item)">Add to Cart</button>
  </ul>
</div>

Cart Component

@Component({
  selector: 'app-cart',
  templateUrl: './cart.component.html',
  styleUrls: ['./cart.component.css']
})
export class CartComponent {
  @Input() products: Item[] = [];

  addProduct(product: Item) {
    this.products.push(product);
  }
}

Parent Component (responsible for handling the business logic)

@Component({
  selector: 'app-root',
  template: `
    <app-items-list [items]="items" (addProduct)="addProduct($event)"></app-items-list>

    <app-card [products]="cartProducts"></app-card>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  readonly items = this.storeService.getItems();

  cartProducts: Item[] = [];

  constructor(private storeService: StoreService) {}

  addProduct(product: Item) {
    this.cartProducts.push(product);
  }
}

Your items list and cart components are dumb components, meaning they don't know where the data is coming from and they should stay that way (the dumb components are also reusable components). That is the role of the parent component.

The parent is fetching the data through a service and passes it to the items list component through an input. The items list component emits an event when the Add Product button is clicked and sends the result to the parent. The parent is responsible to distribute the selected products to the cart component.

CodePudding user response:

The most universal cross component solution is to use Subject. First create a service that will be used for communication (ng g service foo). Then create new subject on the service:

addProductSub = new Subject;

Import this new service to parent and to child component. To emit, just call next method on your subject and pass as argument what ever you wish to send:

this.importedService.addProductSub.next(product)

On child component, subscribe to subject and describe what to do when new data is received.

this.importedService.addProductSub.subscribe((data) => { this.productEmptyArray = data; })

To prevent memory leakage assign subsciption (child component) to a variable and call .unsubscribe() in ngOnDestroy lifecycle hook

CodePudding user response:

You can't emit a value from a parent to its child component. What you can do is:

  • Use an @Input() to pass data from the parent to a child
  • Use an @Output() to send data from a child to the parent

Here is a StackBlitz demo.

  • Related