【问题标题】:Angular 6 Expression has changed after it was checked. Previous value: 'null: 3'Angular 6 Expression 在检查后发生了变化。上一个值:'null:3'
【发布时间】:2019-01-12 18:28:51
【问题描述】:

我想我只是不明白 ngFor 是如何工作的。为什么角度检查值这么多次?请记住,我希望更新值。请解释一下,因为我完全迷路了。

提供一些背景信息:

我有一个产品页面,用于检查购物车项目并显示特定产品的购物车中有多少项目。当前显示 11,此时购物车中只有两件商品。

购物车作为一个对象保存在 localStorage 中,其中列出了用户添加到购物车中的商品。我可以在 Chrome 开发工具>应用程序>购物车中看到 - 对象及其包含的项目数。

以下是我的 Product 模型,与我的购物车对象的结构相同:

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;
  }
}

以下是从购物车服务调用 getTotaltItems(item) 方法的 html,以及来自我的产品服务的过滤产品,位于产品组件的构造函数中:

 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>

【问题讨论】:

  • 我在发布的代码中没有看到任何ngFor。我想“卡片”在里面?
  • @Jeto,感谢您的回复 - ngFor 在我粘贴的代码上方 - - 就像任何我制作的其他 ngFor
  • 什么是filteredProducts?你在问为什么你的循环没有停止,所以我觉得这是一个重要的分享。该错误表明您正在循环期间修改其内容。在任何情况下,您都应该编辑您的问题,而不是在评论中发布它。另外,如果可以的话,考虑创建一个StackBlitz(但也许我们可以提供帮助)。
  • @Jeto,感谢您的回复。 filtersProducts 是从我的产品服务返回的内容。产品加载得很好。我只希望购物车中的商品出现在每个产品下。在这种情况下,购物车中只有两件商品,但角度数为 11。有十五种产品。请专注于 getTotalItems(item) 方法。如果我从 html 中删除它,产品加载正常,没有任何错误,再次感谢您
  • @Jeto,我已经更新了帖子以提供更多信息

标签: angular6 ngfor


【解决方案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

【讨论】:

  • 感谢您帮助@jeto 我正在阅读,这导致我发现您的声明“Angular 将检测到用于显示它的方法也修改了它,这是被禁止的”是正确的。结果,我从 html 中删除了该函数,但我仍然没有收到正确的 item.counter。我尝试了你的两个建议。起初,当我点击一个产品的“addToCart”按钮时,它看起来像是在工作,但是,如果我点击另一个产品,第一个产品的项目编号会增加三倍。我确实想感谢你至少帮助我理解了原因
  • @kronus 不客气!好吧,我得看看addToCart 的代码,因为从你发布的代码中我看不出点击它会做什么。
【解决方案2】:

我必须重构每件事。感谢@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));
}

就是这样

【讨论】:

    猜你喜欢
    • 2021-01-04
    • 2018-11-17
    • 1970-01-01
    • 2021-04-22
    • 2017-12-27
    • 2019-01-05
    • 1970-01-01
    相关资源
    最近更新 更多