Angular视图数据未更新问题:购物车总额无法实时刷新
嘿,别担心,新手刚接触Angular的数据绑定机制时,很容易遇到这种问题!咱们先搞清楚问题根源,再一步步解决它~
问题根源
你在组件的ngOnInit里把this.total赋值为foodService.getTotal()返回的数值,但数值是原始类型——一旦赋值,组件里的total就和Service里的totalAmount彻底断开了联系。Service里的总额变化时,组件里的total不会自动同步更新。
而购物车数组cart能正常更新,是因为数组是引用类型,组件里的cart和Service里的cart指向同一个内存地址,所以数组内容变化时视图能感知到。
解决方案
方法1:快速临时解决(简单但不推荐用于复杂场景)
直接在模板里调用Service的getTotal()方法,这样每次Angular执行变更检测时都会获取最新的总额。
修改组件代码
把Service改成public(因为模板需要访问组件的公共属性):
constructor(public foodService: DataService) { }
然后删掉ngOnInit里的this.total = this.foodService.getTotal();,组件里的total变量也可以直接去掉。
修改模板代码
把原来的{{ total | currency: "INR" }}改成:
<h1><span>{{ foodService.getTotal() | currency: "INR" }}</span></h1>
注意:这种方法虽然简单,但每次变更检测都会调用
getTotal(),如果后续业务复杂,可能会影响性能,所以更推荐下面的响应式方案。
方法2:响应式方案(Angular最佳实践)
用BehaviorSubject来管理总额的变化,让组件订阅这个数据流,自动同步最新值。
修改DataService代码
引入BehaviorSubject,把原来的totalAmount换成可观察对象:
import { Injectable } from '@angular/core'; import { groupBy, remove, find } from "lodash"; import { foodItems } from "./food-items.constants" import { Router } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DataService { private foodz = foodItems; private cart = []; // 用BehaviorSubject存储总额,初始值为0 private totalAmountSubject = new BehaviorSubject<number>(0); // 暴露给外部订阅的可观察对象 public totalAmount$ = this.totalAmountSubject.asObservable(); constructor(private router: Router) { } // 获取当前总额的同步值 getTotal() { return this.totalAmountSubject.value; } getCart() { return this.cart; } getFoodItems() { let items = groupBy(this.foodz, food => food.cuisine); return items; } addItemToCart(item) { const price = Number(item.pricePerUnit); let cartItem = find(this.cart, cartObject => cartObject.itemName === item.itemName); if (cartItem) { cartItem.quantity++; cartItem.Amount = cartItem.quantity * price; } else { this.cart.push({ itemName: item.itemName, quantity: 1, Amount: price }); } // 更新总额的数据流 this.totalAmountSubject.next(this.totalAmountSubject.value + price); alert(this.totalAmountSubject.value); return true; } removeItemFromCart(item) { const price = Number(item.pricePerUnit); let cartItem = find(this.cart, cartObject => cartObject.itemName === item.itemName); if (cartItem) { if (cartItem.quantity === 1) { remove(this.cart, cObject => cObject.itemName === item.itemName); } else { cartItem.quantity--; cartItem.Amount = cartItem.quantity * price; } // 更新总额的数据流 this.totalAmountSubject.next(this.totalAmountSubject.value - price); alert(this.totalAmountSubject.value); } return true; } }
修改AddCardComponent代码
订阅总额的数据流,在组件销毁时取消订阅防止内存泄漏:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { DataService } from '../data.service'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-add-card', templateUrl: './add-card.component.html', styleUrls: ['./add-card.component.css'] }) export class AddCardComponent implements OnInit, OnDestroy { cart = []; total: number; private totalSubscription: Subscription; constructor(private foodService: DataService) { } ngOnInit() { this.cart = this.foodService.getCart(); // 订阅总额变化,自动更新组件的total值 this.totalSubscription = this.foodService.totalAmount$.subscribe(amount => { this.total = amount; }); } ngOnDestroy() { // 销毁订阅,避免内存泄漏 if (this.totalSubscription) { this.totalSubscription.unsubscribe(); } } }
模板代码保留原样(注意修正一个小错误)
你的模板里有个无效的表达式{{ cart.total }}——cart是数组,没有total属性,直接删掉这个部分就好:
<div class="menu text-center" > <br> <h1><span>{{ total | currency: "INR" }}</span></h1 > <br> </div> <br> <div class="menu" style="margin-bottom: 30px" > <div class="heading text-center" > <span>Order Details</span> </div> <div class="item" *ngFor="let item of cart" > <span style="width:50%;display:inline-block" > {{ item.itemName }}</span> <span style="width:20%;display:inline-block" > Qua.{{ item.quantity }}</span> <span > {{ item.Amount | currency: "INR" }}</span> </div> </div>
这样修改后,购物车的总额就会实时同步更新啦~
内容的提问来源于stack exchange,提问作者Gagan Deep

