渲染一个特定的V-If,基于Api响应,在Vue列表中。

huangapple go评论68阅读模式
英文:

Render A specific V-If inside of a list with Vue based on Api response

问题

我遇到了一个问题,我知道如何相当容易地解决,但我不太知道如何以 "vue 方式" 或以声明性的方式来处理这个问题。通常情况下,我会在这种情况下使用传统的DOM操作,因为我习惯了这种方式,所以我在尝试按照Vue的方式来做时遇到了一些困难。

问题出在这个包含两个产品的数组中,其中一个产品只有一个库存,所以如果将其添加到购物车中,就不应该再次添加。现在,由于我们循环遍历数组,我有一个条件性显示的错误。在下面的代码中,每个项目都将显示一个错误消息,因为 v-if 条件将评估为 true。

有什么好的方式可以以声明性的方式处理这个问题,而不是以传统的方式?通常情况下,我会传递 $event,获取当前目标,并使用 insertAdjacentHTML。我不明白这在声明性上如何工作,因为我们需要在从API返回响应之后处理 v-if 逻辑。

  <body>
    <div id="app">
      <div v-for="(product, index) in products" class="product__wrapper" style="position: relative; background: #ccc; margin: 24px;">
        <p>{{ product.title }}</p>
        <button @click="addToCart(product.id)">add to cart</button>
        <div v-if="lastItemIsInCart" class="error">All items are currently in your cart</div>
      </div>
    </div>

    <script type="module">
    Vue.createApp({
      name: 'test-app',
      data() {
        return {
          products: [],
          lastItemIsInCart: null
        }
      },
      mounted() {
        fetch('/products')
          .then(res => res.json())
          .then(data => {
            this.products = data.products
          })
      },
      methods: {
        addToCart(productId) {

          fetch(`/cart/add/${productId}`)
            .then(res => res.json())
            .then(data => {
               // 对这个API而言是唯一的
               if (data.status !== 200) {
                 throw new Error(data.description)
               }
            })
            .catch(err => {
              console.error(err.message) // 所有XYZ产品都在您的购物车中
              // 设置this.lastItemIsInCart为true,以便特定产品的v-if不会对列表中的每个项目都变为true
            })

        }
      }
    }).mount('#app')
    </script>
  </body>

请注意,我没有翻译代码部分,只翻译了您的问题描述和相关注释。

英文:

I ran into an issue that I know how to solve pretty easily, but I don't really know how to handle this the "vue way" or in a declarative way that is. I usually would just use traditional DOM manipulation in this case and since that is what I am so used to, I'm having a bit of a hard time trying to do this the way it should be done in vue.

The problem is in this array of two products, one product only has one in stock, so if it is added to the cart it shouldn't be able to be added again. Now since we loop over the array, I have an error that can be conditionally shown. In the below code, each item will have an error message on it since the v-if condition will evaluate to true.

What would be good way to handle this declaratively, rather than in the traditional way? Usually I would just pass in $event, get the current target and insertAdjacentHTML. I am confused how this would work declaratively since we need to handle the v-if logic after a request comes back from an api.

  <body>
    <div id="app">
      <div v-for="(product, index) in products" class="product__wrapper" style="position: relative; background: #ccc; margin: 24px;">
        <p>{{ product.title }}</p>
        <button @click="addToCart(product.id)">add to cart</button>
        <div v-if="lastItemIsInCart" class="error">All items are currently in your cart</div>
      </div>
    </div>

    <script type="module">
    Vue.createApp({
      name: 'test-app',
      data() {
        return {
          products: [],
          lastItemIsInCart: null
        }
      },
      mounted() {
        fetch('/products')
          .then(res => res.json())
          .then(data => {
            this.products = data.products
          })
      },
      methods: {
        addToCart(productId) {

          fetch(`/cart/add/${productId}`)
            .then(res => res.json())
            .then(data => {
               // unique to this api
               if (data.status !== 200) {
                 throw new Error(data.description)
               }
            })
            .catch(err => {
              console.error(err.message) // all xyz products are in your cart
             // set this.lastItemIsInCart to true for a specific product so that v-if doesn't become true for each item in the list 
            })

        }
      }
    }).mount('#app')
    </script>
  </body>

答案1

得分: 1

相比于将变量`lastItemIsInCart`简单地设为所有产品的`true/false`我会考虑将其设为一个对象包含每个产品获取的成功/错误状态

类似于
```html
<div id="app">
  <div v-for="(product, index) in products" class="product__wrapper" style="position: relative; background: #ccc; margin: 24px;">
    <p>{{ product.title }}</p>
    <button @click="addToCart(product.id)">加入购物车</button>
    <div v-if="product.id in productState" class="error">{{ productState[product.id] }}</div>
  </div>
  
</div>
data() {
  return {
    productState: {},
  }
},
methods: {
  addToCart(productId) {
    setTimeout(() => {
      if (quantity == 1) {
        this.productState[productID] = '所有商品当前都在您的购物车中'
      }
    }, 2000)
  }
}

然后,您可以选择轻松地通过更新产品的状态来扩展功能,这将反过来显示productState中是否存在该id。

(注意:尽管对于v-if来说简单的in测试是可以的,但如果这变得更复杂,最好将其移出到一个计算属性中)。


<details>
<summary>英文:</summary>
Instead of making the variable `lastItemIsInCart` a simple `true/false` for all products, I would consider making this an object containing the success/error state for each product fetch.
Something like:

<div id="app">
<div v-for="(product, index) in products" class="product__wrapper" style="position: relative; background: #ccc; margin: 24px;">
<p>{{ product.title }}</p>
<button @click="addToCart(product.id)">add to cart</button>
<div v-if="product.id in productState" class="error">{{ productState[product.id] }}</div>
</div>

</div>


data() {
return {
productState: {},
}
},
methods: {
addToCart(productId) {
setTimeout(() => {
if quantity == 1 {
this.productState[productID] = 'All items are currently in your cart'
}
}, 2000)
}
}


You then have the option to easily extend the functionality by updating the state for a product, which in turn will show if the id is in `productState`.
(Note: while a simple `in` tets is fine for a `v-if` if this is ever more complex it would be worth moving out into a computed property).
</details>
# 答案2
**得分**: 0
根据您的评论,如果我理解正确,您想要仅在API声明为“已添加”的产品下显示错误。
因此,一种方法是在API响应后,您可以将`lastItemIsInCart`变量分配为缺货的产品的ID,然后在模板中,您可以仅在ID与`lastItemIsInCart`匹配的产品下显示错误消息。
这是一个演示,将为产品2和3显示错误,但允许添加产品1。
```html
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="app">
TOTAL PRODUCTS IN CART - {{ cartItemsCount }}
<div v-for="(product, index) in products" class="product__wrapper" style="position: relative; background: #ccc; margin: 24px;">
<p>{{ product.title }}</p>
<button @click="addToCart(product.id)">add to cart</button>
<div v-if="lastItemIsInCart == product.id" class="error">All items are currently in your cart</div>
</div>
</div>

希望对您有所帮助。

英文:

As per your comment, if I catch you correctly, you want to show the error only under the product which is declared as "Already Added" by the API.

so, one way would be that after the API response, you can assign your lastItemIsInCart variable with the value of that product's id which is out of stock, and then in the template, you can show the error message only under the product whose id is matched with the lastItemIsInCart.

Here is a demo which will display error for product 2 and 3 but allow product 1 to add.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

Vue.createApp({
name: &#39;test-app&#39;,
data() {
return {
lastItemIsInCart: null,
cartItemsCount: 0,
products: [
{
title: &#39;Product 1&#39;,
id: 1,
},
{
title: &#39;Product 2&#39;,
id: 2,
},
{
title: &#39;Product 3&#39;,
id: 3,
}
],
}
},
methods: {
addToCart(productId) { 
// Suppose API is returning an error for product 2 and 3 that these items are already added then assign lastItemIsInCart variable with the product&#39;s id which is causing the error.
if([2, 3].includes(productId)) {
this.lastItemIsInCart = productId
} else {
this.cartItemsCount++;
}
}
}
}).mount(&#39;#app&#39;)

<!-- language: lang-css -->

.error {
color: red;
}

<!-- language: lang-html -->

&lt;script src=&quot;https://unpkg.com/vue@3/dist/vue.global.prod.js&quot;&gt;&lt;/script&gt;
&lt;div id=&quot;app&quot;&gt;
&lt;div id=&quot;app&quot;&gt;
TOTAL PRODUCTS IN CART - {{ cartItemsCount }}
&lt;div v-for=&quot;(product, index) in products&quot; class=&quot;product__wrapper&quot; style=&quot;position: relative; background: #ccc; margin: 24px;&quot;&gt;
&lt;p&gt;{{ product.title }}&lt;/p&gt;
&lt;button @click=&quot;addToCart(product.id)&quot;&gt;add to cart&lt;/button&gt;
&lt;div v-if=&quot;lastItemIsInCart == product.id&quot; class=&quot;error&quot;&gt;All items are currently in your cart&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

<!-- end snippet -->

答案3

得分: 0

&lt;body&gt;
    &lt;div id=&quot;app&quot;&gt;
      &lt;div v-for=&quot;(product, index) in products&quot; class=&quot;product__wrapper&quot; style=&quot;position: relative; background: #ccc; margin: 24px;&quot;&gt;
        &lt;p&gt;{{ product.title }}&lt;/p&gt;
        &lt;button @click=&quot;addToCart(product.id)&quot;&gt;add to cart&lt;/button&gt;
        &lt;div v-if=&quot;addToCartError &amp;&amp; addToCartError.id === product.id&quot; class=&quot;error&quot;&gt;{{ addToCartError.message }}&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;script type=&quot;module&quot;&gt;
    Vue.createApp({
      name: &#39;test-app&#39;,
      data() {
        return {
          products: [],
          addToCartError: null,
          timeout_id: undefined
        }
      },
      mounted() {
        fetch(&#39;/products&#39;)
          .then(res =&gt; res.json())
          .then(data =&gt; {
            this.products = data.products
          })
      },
      methods: {
        addToCart(productId) {

          fetch(`/cart/add/${productId}`)
            .then(res =&gt; res.json())
            .then(data =&gt; {
               // unique to this api
               if (data.status !== 200) {
                 throw new Error(data.description)
               }
            })
            .catch(err =&gt; {
              console.error(err.message) // all xyz products are in your cart
              this.addToCartError = {
                message: err.message,
                id: productId
              }

              if (this.timeout_id) {
                clearTimeout(this.timeout_id)
                this.timeout_id = undefined
              }

              this.timeout_id = setTimeout(() =&gt; {
                this.addToCartError = null
              }, 4000)
            })

        }
      }
    }).mount(&#39;#app&#39;)
    &lt;/script&gt;
  &lt;/body&gt;
英文:

I finally got close to what I am looking for, this could be improved because of the setTimeout but here is a good way to do it:

 &lt;body&gt;
    &lt;div id=&quot;app&quot;&gt;
      &lt;div v-for=&quot;(product, index) in products&quot; class=&quot;product__wrapper&quot; style=&quot;position: relative; background: #ccc; margin: 24px;&quot;&gt;
        &lt;p&gt;{{ product.title }}&lt;/p&gt;
        &lt;button @click=&quot;addToCart(product.id)&quot;&gt;add to cart&lt;/button&gt;
        &lt;div v-if=&quot;addToCartError &amp;&amp; addToCartError.id === product.id&quot; class=&quot;error&quot;&gt;{{ addToCartError.message }}&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;script type=&quot;module&quot;&gt;
    Vue.createApp({
      name: &#39;test-app&#39;,
      data() {
        return {
          products: [],
          addToCartError: null,
          timeout_id: undefined
        }
      },
      mounted() {
        fetch(&#39;/products&#39;)
          .then(res =&gt; res.json())
          .then(data =&gt; {
            this.products = data.products
          })
      },
      methods: {
        addToCart(productId) {

          fetch(`/cart/add/${productId}`)
            .then(res =&gt; res.json())
            .then(data =&gt; {
               // unique to this api
               if (data.status !== 200) {
                 throw new Error(data.description)
               }
            })
            .catch(err =&gt; {
              console.error(err.message) // all xyz products are in your cart
              this.addToCartError = {
                message: err.message,
                id: productId
              }

              if (this.timeout_id) {
                clearTimeout(this.timeout_id)
                this.timeout_id = undefined
              }

              this.timeout_id = setTimeout(() =&gt; {
                this.addToCartError = null
              }, 4000)
            })

        }
      }
    }).mount(&#39;#app&#39;)
    &lt;/script&gt;
  &lt;/body&gt;

huangapple
  • 本文由 发表于 2023年2月16日 18:43:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75471114.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定