简体   繁体   中英

Angular 2+ Catch event when shared object between components changes

I have 3 components (Sales, ProductSales, ProductTotals) that share an array of products but the ProductsTotal component is not reading the changes of the array. The father component (Sales) initializes the array of products and set as intnput for the child components, the ProductSales component changes the value of the array of products (add, remove, change properties like unit price, etc) and the ProductTotals component read the array and constantly does the operations to obtain the total of the sum of the unit prices of the products and displays them.

SalesComponent.html

<app-products-sale-total class="full-wid" [products]="products"></app-products-sale-total>
<app-products-sale class="full-wid" [products]="products"></app-products-sale>

To display the totals i did this ProductTotals.html

{{products.length}}
<strong>Taxes:</strong> {{taxes|currency}} 
<strong>Subtotal:</strong> {{subtotal|currency}} 
<strong>Total:</strong> {{total|currency}} 

ProductsTotal.ts

import { Component, OnInit, Input, OnChanges, OnDestroy, SimpleChange } from '@angular/core';
import { ProductSale } from '../product-sale-detail/product-sale-detail.component';

@Component({
  selector: 'app-products-sale-total',
  templateUrl: './products-sale-total.component.html',
  styleUrls: ['./products-sale-total.component.scss']
})
export class ProductsSaleTotalComponent implements OnChanges {

  @Input() products: Array<ProductSale> = [];
  total: number = 0;
  subtotal: number = 0;
  taxes: number = 0;
  constructor() {
   }

  ngOnChanges(changes: import("@angular/core").SimpleChanges): void {
    calculateTotalFields();
    console.log(changes);
  }

  calculateTotalFields(): void {
    let total = 0;
    let subtotal = 0;
    let taxes = 0;
    this.products.forEach(x => {
      const totalProduct = x.UnitPrice * x.Quantity;
      const taxesProduct = totalProduct * 0.16;
      total += totalProduct;
      subtotal += totalProduct - taxesProduct;
      taxes += taxesProduct;
    });
    this.total = total;
    this.taxes = taxes;
    this.subtotal = subtotal;
  }
}

The problem is that only enters the ngOnChanges method once even though the object of products is changing.

The result of updating products in ProductsSalesComponent:

在 ProductsSalesComponent 中更新产品的结果

Edit I've created a project in StackBlitz to show the problem about how the ngOnChange event of the component 'ProductSaleTotal' is not fire when the object 'products' changes https://stackblitz.com/edit/angular-fcevey?file=src%2Fapp%2Fproducts-sale-total%2Fproducts-sale-total.component.ts

app-products-sale-total and app-products-sale are both different component, so to communicate with them you need to use @Output() from app-products-sale and pass array of object to app-products-sale-total ... Complete working example you can find out here in this StackBlitz Link

Your ProductSaleDetailComponent.ts is...

export class ProductSaleDetailComponent implements OnInit {
    @Input() product: ProductSale;
    @Input () index: number;
    @Output() productData = new EventEmitter();
    constructor() { }

    ngOnInit() {
       this.product.Quantity = 1;
    }

    changeInput(p){
        this.productData.emit({event: 'add',value:p, index : this.index});
    }
}

Your ProductsSaleComponent template is...

    <button type="button" (click)="addProduct()">Add Product!</button>
        <div *ngFor="let product of products; let i=index">
             <div> 
                 <app-product-sale-detail [product]="product" [index]="i" (productData)= "productOutput($event)" ></app-product-sale-detail>
             </div>
        <div>
            <button aria-label="Delete" color="warn" (click)="removeProduct(i)">
              Remove Product
            </button>
        </div>
    </div>

Your ProductSaleComponent.ts is...

export class ProductsSaleComponent implements OnInit {

   @Input() products: Array<ProductSale> = [];
   @Output() ProductOutputData = new EventEmitter(); 
   constructor() { }

   ngOnInit() {
      console.log('product Sale',new ProductSale());
   }

   addProduct() {
      this.products.push(new ProductSale());
   }

   removeProduct(index: number) {
      this.ProductOutputData.emit({event:'remove', index: index, value : this.products[index]} );
      this.products.splice(index, 1);
   }
   productOutput(event){
      this.ProductOutputData.emit(event);
   }
}

So, basically you need chain of @Input() and @Output()` to communicate between child-parent respectively..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM