Home > Net >  Getting wrong total value because of multiple change deduction how to prevent?
Getting wrong total value because of multiple change deduction how to prevent?

Time:10-06

I am getting wrong value, because of multiple change deduction. how to prevent?

app.component.ts :

import { Component, OnInit } from "@angular/core";

interface PropsData {
  productName: string;
  value: number;
  count: number;
  details: (value1: number, value2: number) => number;
}

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  products: PropsData[];
  allTotal: number = 0;

  ngOnInit() {
    this.products = [
      {
        productName: "sugar",
        value: 20,
        count: 1,
        details: (value1, value2) => {
          return this.grandTotal(value1, value2);
        }
      },
      {
        productName: "salt",
        value: 40,
        count: 1,
        details: (value1, value2) => {
          return this.grandTotal(value1, value2);
        }
      },
      {
        productName: "jackery",
        value: 70,
        count: 1,
        details: (value1, value2) => {
          return this.grandTotal(value1, value2);
        }
      }
    ];
  }

  updateCount(product: PropsData) {
    product.count  ;
  }

  updateAllTotal(itemTotal: number): void {
    this.allTotal = this.allTotal   itemTotal;
  }

  grandTotal(value1, value2): number {
    const total = value1 * value2;
    this.updateAllTotal(total);
    return total;
  }
}

html:

<div>
  <div *ngFor="let product of products">
    <h1>{{product.productName}}</h1>
    <p>{{product.count}}</p>
    <p>Total: {{product.details(product.count, product.value)}}</p>
    <p><button (click)="updateCount(product)">Add Product</button></p>
  </div>
  <h2>Grand Total: {{allTotal}}</h2>
</div>

Live Demo

CodePudding user response:

As a general rule binding to a method should be used sparingly: in your case I would refactor the code in order to avoid the binding to product.details().

First solution

A simple way to do that is to declare an event emitter notifying when you're updating product.count: product.details could become a simple property (instead of a method) and gets updated in the event handler.

    interface PropsData {
      productName: string;
      value: number;
      count: number;
      details: number;
    }

This is how your component might look like:

products: PropsData[];
  allTotal: number = 0;
  onProductUpdate = new EventEmitter<PropsData>();

  ngOnInit() {
    this.products = [
      {
        productName: "sugar",
        value: 20,
        count: 1,
        details: 20 // Needs to be manually initialized, as it's not a function anymore
      },
      {
        productName: "salt",
        value: 40,
        count: 1,
        details: 40
      },
      {
        productName: "jackery",
        value: 70,
        count: 1,
        details: 70
      }
    ];

    this.updateAllTotal(); // Compute allTotal the first time
    
    // On every product update compute product.details and allTotal
    this.onProductUpdate.subscribe((product) => {
      product.details = this.getProductDetails(product);
      this.updateAllTotal();
    });
  }

  updateCount(product: PropsData) {
    product.count  ;
    this.onProductUpdate.next(product);
  }

  updateAllTotal(): void {
    this.allTotal = 0;
    this.products.forEach(
      (item) => (this.allTotal = this.allTotal   item.details)
    );
  }

  getProductDetails(product): number {
    return product.count * product.value;
  }

And this could be your template now (no function binding anymore):

<div>
  <div *ngFor="let product of products">
    <h1>{{product.productName}}</h1>
    <p>{{product.count}}</p>
    <p>Total: {{product.details}}</p>
    <p><button (click)="updateCount(product)">Add Product</button></p>
  </div>
  <h2>Grand Total: {{allTotal}}</h2>
</div>

CodeSandbox

Alternative solution

Depending on how complex your real use case is, a different approach would be to encapsulate in the product itself the logic for updating its details every time value or count changes. For this you need to change PropsData to a class:

class PropsData {
  productName: string;
  private _value: number;
  private _count: number;
  public get value() {
    return this._value;
  }
  public set value(value) {
    this._value = value;
    this.details = this._value * this._count;
  }
  public get count() {
    return this._count;
  }
  public set count(value) {
    this._count = value;
    this.details = this._value * this._count;
  }
  details: number;

  constructor(name: string, value: number, count: number) {
    this.productName = name;
    this.count = count;
    this.value = value;
  }
}

As you can see, value and count setters take care of updating details as well. This simplifies the component's logic:

products: PropsData[];
  allTotal: number = 0;

  ngOnInit() {
    this.products = [
      new PropsData("sugar", 20, 1),
      new PropsData("salt", 40, 1),
      new PropsData("jackery", 70, 1)
    ];
    this.updateAllTotal();
  }

  updateCount(product: PropsData) {
    product.count  ;
    this.updateAllTotal();
  }

  updateAllTotal(): void {
    this.allTotal = 0;
    this.products.forEach(
      (item) => (this.allTotal = this.allTotal   item.details)
    );
  }

CodeSandbox

CodePudding user response:

I think it's this line of code, Just get rid of it

    <p>Total: {{product.details(product.count, product.value)}}</p>

  • Related