检查后,Angular 6表达式已更改。上一个值:'null:3'

时间:2019-01-12 18:28:52

标签: angular6 ngfor

我想我只是不了解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,计数器也会增加。就我而言,购物车中只有两项:

As you can see by the screenshot, the product page loads just fine, with the exception that it is not showing the number items in the shopping cart correctly

This is a screenshot of Chrome dev tools showing the contents of the cart and  the error

如您所见,一旦通过第二项,它将继续检查,从而导致错误。

我的问题基本上是,一旦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>

2 个答案:

答案 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));
}

就是这样