我想我只是不了解ngFor的工作原理。为什么角度要多次检查该值?请记住,我希望更新值。请解释,因为我感到完全迷失了。
提供一些背景信息:
我有一个产品页面,该页面检查购物车中的物品并显示特定产品的购物车中有多少物品。当前,当购物车中只有两个项目时,它将显示11。
购物车作为一个对象存储在localStorage中,该对象列出了用户添加到购物车中的商品。我可以在Chrome开发者工具>应用程序>购物车中看到该对象及其包含的项目。
以下是我的产品模型,与我的购物车对象的结构相同:
export interface Product {
product: {
id: number;
name: string;
category: string;
price: string;
ts: string;
product_image_id: number;
product_image: string;
enabled: boolean;
counter: number;
};
}
以下是getTotalItems(item)方法,该方法计算特定产品的购物车中有多少个产品,当与产品ID匹配时,基本上会增加item.counter:
getTotalItems(item) {
if (localStorage.getItem("cart")) {
for (let u = 0; u < this.cartObj.cart.products.product.length; u++) {
if (item.id === this.cartObj.cart.products.product[u].id) {
item.counter += this.cartObj.cart.products.product[u].counter;
}
}
} else {
item.counter = 0;
}
return item.counter;
}
请注意,item.counter正在for循环中更新,但是for循环仅适用于购物车中的两个项目。我想知道为什么angular会尝试更新11次:
for (let u = 0; u < this.cartObj.cart.products.product.length; u++) {
if (item.id === this.cartObj.cart.products.product[u].id) {
item.counter += this.cartObj.cart.products.product[u].counter;
}
}
以下是从购物车服务中调用geTotaltItems(item)方法的html,以及来自我的产品服务中位于产品组件构造函数中的filteredProducts:
this.prdSrvc.getAllProducts().subscribe(resProd => {
this.filteredProducts = this.products = resProd.products;
<ng-container *ngFor="let item of filteredProducts; let i = index">
<div class="card-body">
<h5 class="card-title">{{ item.name }}</h5>
<p class="card-text">{{ item.category }}</p>
<p class="card-text">{{ item.price | currency }}</p>
</div>
<div class="card-footer">
<div class="row no-gutters">
<div class="col-2">
<button
(click)="removeFromCart(item)"
class="btn btn-secondary btn-block"
>
-
</button>
</div>
<div class="col text-center">
{{ this.cartSrvc.getTotalItems(item) }} in cart
</div>
<div class="col-2">
<button
(click)="addToCart(item)"
class="btn btn-secondary btn-block"
>
+
</button>
</div>
</div>
</div>
在getTotalItems方法中,放置以下console.log:
console.log("item.id: " + item.id + "item.counter: " + item.counter);
起初,它工作得很好,找到了item.id,随着在购物车中找到匹配的ID,计数器也会增加。就我而言,购物车中只有两项:
如您所见,一旦通过第二项,它将继续检查,从而导致错误。
我的问题基本上是,一旦for循环遍历了购物车中的所有项目,为什么它不会停止?在这种情况下,只有两个项目,为什么Angular会继续检查这两个项目?
预先感谢
顺便说一句,我还试图通过执行以下操作来限制调用getTotalItems的数量,使其结果相同:
<div
*ngIf="item.counter > 0; else justZero"
class="col text-center"
>
{{ this.cartSrvc.getTotalItems(item) }} in cart
</div>
<ng-template #justZero>
<div class="col text-center">0 in cart</div>
</ng-template>
答案 0 :(得分:1)
该问题与ngFor
无关。
在getTotalItems
函数中,您是从模板中调用的,因此必须确保使用给定项目调用两次会产生(返回)相同的结果。否则,Angular将检测到用于显示它的方法也对其进行了修改,这是禁止的。
对于您来说,连续两次使用相同的项调用它不会产生相同的结果,因为您从未重置过item.counter
。
您有两种解决方法。
将您的getTotalItems
代码更改为以下内容:
getTotalItems(item) {
item.counter = 0;
if (localStorage.getItem("cart")) {
for (let u = 0; u < this.cartObj.cart.products.product.length; u++) {
if (item.id === this.cartObj.cart.products.product[u].id) {
item.counter += this.cartObj.cart.products.product[u].counter;
}
}
}
return item.counter;
}
在这里,您总是将计数器重置为0,并且仅当本地存储中有购物车时才将其递增。
第二种方法要求您在调用模板之前存储计数器 ,因此建议使用此方法,因为通常认为在视图中调用组件方法是不明智的做法(因为它们不仅会被调用一次)
您的组件代码变为:
this.filteredProducts.forEach(item => item.count = this.getTotalItems(item));
然后将getTotalItems
更改为此:
getTotalItems(item) {
let count = 0;
if (localStorage.getItem("cart")) {
for (let u = 0; u < this.cartObj.cart.products.product.length; u++) {
if (item.id === this.cartObj.cart.products.product[u].id) {
count += this.cartObj.cart.products.product[u].counter;
}
}
}
return count;
}
然后,从您的角度来看,您可以简单地执行以下操作:
{{ item.count }} in cart
答案 1 :(得分:0)
我不得不重构所有东西。感谢@jeto,并阅读了几篇文章,我了解到像这样将函数放置在html中不是一种好习惯:
<div class="col text-center">
{{ this.cartSrvc.getTotalItems(item) }} in cart
</div>
再次,正如@jeto所说,“ Angular将检测到用于显示它的方法也对其进行了修改,这是被禁止的”
结果,我报废了几乎所有东西。现在,购物车服务唯一要做的就是读取存储购物车的localStorage.getItem('cart'),然后使用内容填充shoppingCartObj,然后将其用于显示商品购物车。
以下是我现在的html产品:
<div class="col">
<div class="row">
<ng-container *ngFor="let item of filteredProducts; let i = index">
<div class="col">
<div class="card product-card">
<img
class="card-img-top"
src="{{ oshopUrl + 'uploads/' + item.product_image }}"
alt="{{ item.name }}"
/>
<div class="card-body">
<h5 class="card-title">{{ item.name }}</h5>
<p class="card-text">{{ item.category }}</p>
<p class="card-text">{{ item.price | currency }}</p>
</div>
<div class="card-footer">
<div class="row no-gutters">
<div class="col-2">
<button
(click)="removeFromCart(item, '-')"
class="btn btn-secondary btn-block"
>
-
</button>
</div>
<div class="col text-center">{{ item.counter }} in cart</div>
<div class="col-2">
<button
(click)="addToCart(item, '+')"
class="btn btn-secondary btn-block"
>
+
</button>
</div>
</div>
</div>
</div>
</div>
</ng-container>
<div *ngIf="(i + 1) % 2 === 0" class="w-100"></div>
</div>
</div>
以下是与此讨论相关的产品组件:
addToCart(item, operator) {
if (!localStorage.getItem("cart")) {
if (item.counter === null || typeof item.counter === "undefined") {
item.counter = 1;
}
this.text = "";
this.createCart(item);
console.log("makes it to createCart");
} else {
if (item.counter === null || typeof item.counter === "undefined") {
item.counter = 0;
}
this.updateCart(item, operator);
console.log("makes it to updateCart");
}
this.incrementCartState();
}
createCart(item) {
if (!localStorage.getItem("cart")) {
this.shoppingCartObj = this.addItemToShoppingCart(item);
localStorage.setItem("cart", JSON.stringify(this.shoppingCartObj));
}
}
addItemToShoppingCart(item: Product) {
if (this.text === "") {
this.text = this.makeOrderId();
console.log("this.text: ", this.text);
}
this.productArr.push(item);
this.shoppingCartObj = {
cart: {
user_id: this.loginSrvc.loginObj.uName,
orders_id: this.text,
ts: TimeSt.mTstamp(),
products: {
product: this.productArr
}
}
};
this.getPriceTotal();
// this.updateFilteredProducts();
return this.shoppingCartObj;
}
updateCart(item, operator) {
// console.log("from updateCart item: ", item);
const index = this.productArr.indexOf(item);
if (index > -1) {
this.productArr.splice(index, 1);
}
if (operator === "+") {
item.counter += 1;
} else {
item.counter -= 1;
}
this.productArr.splice(this.productArr.length, 0, item);
this.shoppingCartObj = {
cart: {
user_id: this.loginSrvc.loginObj.uName,
orders_id: this.text,
ts: TimeSt.mTstamp(),
products: {
product: this.productArr
}
}
};
this.getPriceTotal();
// console.log("this.shoppingCartObj: ", this.shoppingCartObj);
localStorage.setItem("cart", JSON.stringify(this.shoppingCartObj));
}
makeOrderId() {
this.text = "";
this.possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 15; i++) {
this.text += this.possible.charAt(
Math.floor(Math.random() * this.possible.length)
);
}
return this.text;
}
我学到的主要内容是保持简单。在这种情况下,html会读取item.counter,然后先递增/递减计数器,就像我在updateCart中所做的那样,后者确定我们是通过传递的运算符来递增还是递减:
if (operator === "+") {
item.counter += 1;
} else {
item.counter -= 1;
}
我遇到的另一个问题是何时从产品页面转到购物车页面。如果我增加/减少了一个项目,那么购物车突然只包含最后更新的项目。结果,再次感谢@jeto,我每次使用ngOnInit方法时,都会使用他的代码的一部分重新加载购物车中的所有内容:
if (localStorage.getItem("cart")) {
this.filteredProducts.forEach(item => this.moveEverythingToCart(item));
}
moveEverythingToCart(item) {
if (item.counter > 0) {
this.refillCart(item);
}
}
refillCart(item) {
const index = this.productArr.indexOf(item);
if (index > -1) {
this.productArr.splice(index, 1);
}
this.productArr.splice(this.productArr.length, 0, item);
this.shoppingCartObj = {
cart: {
user_id: this.loginSrvc.loginObj.uName,
orders_id: this.text,
ts: TimeSt.mTstamp(),
products: {
product: this.productArr
}
}
};
this.getPriceTotal();
localStorage.setItem("cart", JSON.stringify(this.shoppingCartObj));
}
就是这样