Total Price is not updating when the quantity of p

2019-05-01 17:08发布

问题:

I am working on the Ionic Ecommerce App and using API made in Laravel. I have added the products in the cart but when I am increasing the quantity of products in the cart, the price of product is increasing but the total price is not updating and also when removing the product from the cart, it is not updating the price.

This is my cart.html:

<ion-header>
  <ion-navbar color="primary">
    <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>
      Your Cart
    </ion-title>
  </ion-navbar>

</ion-header>

<ion-content>
  <ion-list *ngFor="let itm of cartItems" class="myitem11">
    <ion-item>
      <ion-thumbnail item-start >
        <img src="{{itm.image}}">
      </ion-thumbnail>
      <h2>{{itm.name}}</h2>
      <p>Actual Price:
        <span [ngStyle]="itm?.discountp === '0' ? {'text-decoration':'none'} : {'text-decoration':'line-through'}">
          ₹{{itm.disprice * itm.count}}
        </span>
      </p>
      <p>Discount: {{itm?.discountp}}%</p>
      <ion-row class="item-count">
        <ion-col class="qty">
            <button (click)="decreaseProductCount(itm)" clear ion-button small color="dark" class="mewbtn11">
              -
            </button>
            <button ion-button small clear color="dark" class="mewbtn11">
              {{itm?.count}}
            </button>
            <button (click)="incrementProductCount(itm)" clear ion-button small color="dark" class="mewbtn11">
              +
            </button>
        </ion-col>
      </ion-row>
      <p>Discounted Price: ₹{{itm.productPrice * itm.count}}</p>
      <button ion-button icon-only clear item-end (click)="removeItem(itm)"><ion-icon class="mycaicon11" name="ios-trash-outline"></ion-icon></button>
    </ion-item>
  </ion-list>
</ion-content>

<ion-footer class="single-footer" ngif="!isEmptyCart">

  <ion-grid>
    <ion-row>
      <ion-col class="addCart" (click)="checkpage()">
        <button color="secondary" full="" ion-button="" round="true">
          {{totalAmount}} Checkout
        </button>
      </ion-col>
    </ion-row>
  </ion-grid>

</ion-footer>

This is my cart.ts:

import { CheckoutPage } from './../checkout/checkout';
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, LoadingController, AlertController } from 'ionic-angular';
import { CartProvider } from "../../providers/cart/cart";


@IonicPage()
@Component({
  selector: 'page-cart',
  templateUrl: 'cart.html',
})
export class CartPage {
 cartItems: any[] = [];
 totalAmount: number = 0;
 isCartItemLoaded: boolean = false;
 isEmptyCart: boolean = true;
 productCount: number = 1;
  constructor(public navCtrl: NavController, public navParams: NavParams, private cartService: CartProvider, public loadingCtrl: LoadingController, private alertCtrl: AlertController, private cdr: ChangeDetectorRef) {
  }

  ionViewDidLoad() {
    console.log('ionViewDidLoad CartPage');
    this.cartService.getCartItems().then((val) => {
      this.cartItems = val;
      console.log(val);
    });
    this.loadCartItems();
  }

  loadCartItems() {
    let loader = this.loadingCtrl.create({
      content: "Wait.."
    });
    loader.present();
    this.cartService
      .getCartItems()
      .then(val => {
        this.cartItems = val;
        if (this.cartItems.length > 0) {
          this.cartItems.forEach((v, indx) => {
            this.totalAmount += parseInt(v.totalPrice);
            console.log(this.totalAmount);
          });
          this.cdr.detectChanges();
          this.isEmptyCart = false;
        }

        this.isCartItemLoaded = true;
        loader.dismiss();
      })
      .catch(err => {});
  }

  removeItem(itm) {
    let alert = this.alertCtrl.create({
      title: 'Remove Product',
      message: 'Do you want to remove this product?',
      buttons: [
        {
          text: 'Cancel',
          role: 'cancel',
          handler: () => {
            console.log('Cancel Clicked');
          }
        },
        {
          text: 'Yes',
          handler: () => {
            this.cartService.removeFromCart(itm).then(() => {
              this.loadCartItems();
            });
          }
        }
      ]
    });
    alert.present();
  }

  checkpage()
  {
    this.navCtrl.push(CheckoutPage);
  }

  decreaseProductCount(itm) {
    if (itm.count > 1) {
      itm.count--;
      this.cdr.detectChanges(); 
    }
  }

  incrementProductCount(itm) {
    itm.count++;
    this.cdr.detectChanges();
  }

}

This is my Cart Service: Provider>Cart>cart.ts:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';

const CART_KEY = 'cartItems';

@Injectable()
export class CartProvider {

  constructor(public http: HttpClient, public storage: Storage) {
    console.log('Hello CartProvider Provider');
  }

  addToCart(productdet) {
    return this.getCartItems().then(result => {
      if (result) {
        if (!this.containsObject(productdet, result)) {
          result.push(productdet);
          return this.storage.set(CART_KEY, result);
        } else {
          let index = result.findIndex(x => x.product_id == productdet.product_id);
          let prevQuantity = parseInt(result[index].count);
          productdet.count = (prevQuantity + productdet.count);
          let currentPrice = (parseInt(productdet.totalPrice));
          productdet.totalPrice = currentPrice;
          result.splice(index, 1);
          result.push(productdet);
          return this.storage.set(CART_KEY, result);
        }

      } else {
        return this.storage.set(CART_KEY, [productdet]);
      }
    })
  }

  removeFromCart(productdet) {
    return this.getCartItems().then(result => {
      if (result) {
        var productIndex = result.indexOf(productdet);
        result.splice(productIndex, 1);
        return this.storage.set(CART_KEY, result);
      }
    })
  }

  removeAllCartItems() {
    return this.storage.remove(CART_KEY).then(res => {
      return res;
    });
  }


  containsObject(obj, list): boolean {
    if (!list.length) {
      return false;
    }

    if (obj == null) {
      return false;
    }
    var i;
    for (i = 0; i < list.length; i++) {
      if (list[i].product_id == obj.product_id) {
        return true;
      }
    }
    return false;
  }

  getCartItems() {
    return this.storage.get(CART_KEY);
  }
}

The problem is that, When I am increasing the quantity in the cart page, it is not updating the total price and also this happens while removing the product. This is the demo of my cart page. It is taking the product original price and that's why it is not able to update the totalprice. I want to totalprice should take from the cart products and when the product quantity is incresed it is update the price and also when the product is removed it will update the price. Any help is much appreciated.

回答1:

I am updating my answer after I went through the code more thoroughly (initially I suspected change detection issues, but in fact I think the issue is how totalAmount variable is handled).

If we follow your code and see what changes totalAmount var: - first it gets set to 0 within cart.ts - then it gets updated every time loadCartItems() method in cart.ts is called, where as this method takes data from persistence (Ionic Storage)

So it is naturally that when you update quantity in these methods:

decreaseProductCount(itm) {
    if (itm.count > 1) {
      itm.count--;
    }
  }

  incrementProductCount(itm) {
    itm.count++;
  } 

That totalAmount is not getting updated as you do not execute any code to do it. To update totalAmount as part of these methods I would propose to add this method:

recalculateTotalAmount() {
    let newTotalAmount = 0;
    this.cartItems.forEach( cartItem => {
        newTotalAmount += (cartItem.productPrice * cartItem.count)
    });
    this.totalAmount = newTotalAmount;
}

Now you can update total price in those methods:

decreaseProductCount(itm) {
        if (itm.count > 1) {
          itm.count--;
          this.recalculateTotalAmount();
        }
      }

incrementProductCount(itm) {
        itm.count++;
        this.recalculateTotalAmount();
      } 

UPDATE:

Also now to address the problem you mentioned in the comments ("remove the product from the cart it is not updating the totalprice and when i added the product from the product page with quantity 2, in the cart it is showing the price for the quantity 1 only and after clicking the quantity button in the cart it is updating the price.") in your loadCartItems you now can leverage same recalc method:

loadCartItems() {
    let loader = this.loadingCtrl.create({
      content: "Wait.."
    });
    loader.present();
    this.cartService
      .getCartItems()
      .then(val => {
        this.cartItems = val;
        if (this.cartItems.length > 0) {
          this.isEmptyCart = false;
          this.recalculateTotalAmount();
        }
        this.isCartItemLoaded = true;
        loader.dismiss();
      })
      .catch(err => {});
  }

PS: I also noticed that in this method you have the same calls to persistence twice:

ionViewDidLoad() {
    console.log('ionViewDidLoad CartPage');
    // I would remove the commented below as your method below does the same work
    //this.cartService.getCartItems().then((val) => {
      //this.cartItems = val;
      //console.log(val);
    //});
    this.loadCartItems();
  }

I think you should remove the first call as loadCartItems does the same work basically.

UPDATE2:

Here in removeItem method you call removeFromCart that returns promises in sequence, while you call loadCartItems after first one. To fix you can wrap two async operations into one promise.

removeItem(itm) {
    let alert = this.alertCtrl.create({
      title: 'Remove Product',
      message: 'Do you want to remove this product?',
      buttons: [
        {
          text: 'Cancel',
          role: 'cancel',
          handler: () => {
            console.log('Cancel Clicked');
          }
        },
        {
          text: 'Yes',
          handler: () => {
            this.cartService.removeFromCart(itm).then(() => {
              this.loadCartItems();
            });
          }
        }
      ]
    });
    alert.present();
  }

Reworked in provider:

removeFromCart(productdet) {
   return new Promise((resolve,reject) => {
this.getCartItems().then(result => {
      if (result) {
        var productIndex = result.indexOf(productdet);
        result.splice(productIndex, 1);
        this.storage.set(CART_KEY, result).then(()=>{ resolve() })
      }
})
})

PSS: I also feel like storing the shopping cart in persistence is fine but in your case it sort of becomes source of truth for your cart. Maybe it is best to have all the cart data in-memory and only lazily persist its data to Ionic Storage.



回答2:

The totalAmount is not updating because your code never updates it ;) There are many ways to go around this. One of them is as Sergey Rudenko suggested, add a function that performs the calculation each time an item count is modified. Another way to do this is to create a pipe which performs actions like so:

import { Pipe, PipeTransform } from "@angular/core";

@Pipe({
    name: "totalAmount",
})

export class TotalAmountPipe implements PipeTransform {
   public transform(cartItems: any): number {
     if(!cartItems || !cartItems.length) {
         return 0;
     }
     return cartItems.reduce( (acc, curr) => { return acc + (curr.count * curr. productPrice)}, 0 );

   }
}

You then need to add this Pipe to your module declarations, and use replace in your html

<button color="secondary" full="" ion-button="" round="true">
          {{totalAmount}} Checkout
</button>

With

<button color="secondary" full="" ion-button="" round="true">
          {{cartItems | totalAmount}} Checkout
</button>