Display a particular V-If condition within a Vue list, contingent upon the API's response

Encountered an issue that I can easily resolve, but not sure how to handle it in the "vue way" or in a declarative manner. Typically, I resort to traditional DOM manipulation, which makes it challenging for me to follow Vue's recommended approach.

The issue arises with a two-product array where one product has only one item in stock. When added to the cart, it should not be possible to add it again. As we iterate over the array, an error message is displayed conditionally on each item due to the v-if evaluation being true.

How can this be dealt with declaratively, rather than conventionally? Normally, I would pass in $event, extract the current target, and use insertAdjacentHTML. However, I'm unsure of how to achieve this declaratively when handling logic after receiving a response 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>

Answer №1

Rather than simplifying the variable lastItemIsInCart to a basic true/false value for all products, it might be beneficial to transform it into an object that stores the success/error status for each product fetch.

For instance:

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

This approach allows for easy expansion of functionality by updating the state of a product, which will then reflect whether the id is present in productState.

(Please note: While a straightforward in check suffices for a v-if, if the logic becomes more intricate, consider moving it to a computed property).

Answer №2

Based on your feedback, it seems like you are looking to display an error message only for products marked as "Already Added" by the API.

One approach would be to update the lastItemIsInCart variable with the id of the out-of-stock product in the API response. Then, in the template, you can selectively show the error message under the product that matches the lastItemIsInCart value.

Below is a example showcasing how errors will be displayed for products 2 and 3 while allowing product 1 to be added:

Vue.createApp({
  name: 'test-app',
  data() {
    return {
      lastItemIsInCart: null,
      cartItemsCount: 0,
      products: [
        {
          title: 'Product 1',
          id: 1,
        },
        {
          title: 'Product 2',
          id: 2,
        },
        {
          title: 'Product 3',
          id: 3,
        }
      ],
    }
  },
  methods: {
    addToCart(productId) { 
      // If API indicates product 2 or 3 already added, assign productId to lastItemIsInCart
      if([2, 3].includes(productId)) {
        this.lastItemIsInCart = productId
      } else {
        this.cartItemsCount++;
      }
    }
  }
}).mount('#app')
.error {
  color: red;
}
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="app">
  <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>
</div>

Answer №3

I have finally come close to finding what I was searching for. Although there may be room for improvement due to the setTimeout function, here is a solid approach:

 <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="addToCartError && addToCartError.id === product.id" class="error">{{ addToCartError.message }}</div>
      </div>
    </div>

    <script type="module">
    Vue.createApp({
      name: 'test-app',
      data() {
        return {
          products: [],
          addToCartError: null,
          timeout_id: undefined
        }
      },
      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 => {
               // specific 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
              this.addToCartError = {
                message: err.message,
                id: productId
              }

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

              this.timeout_id = setTimeout(() => {
                this.addToCartError = null
              }, 4000)
            })

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issues arise when implementing two-way data binding between parent and child components

I'm currently exploring the implementation of two-way data binding between parent and child components. In this case, my child component, phoneInput, accepts a phone number, formats it, and then passes it to the parent using two-way data binding. Over ...

``In a jade view, navigating through JavaScript object properties leads to the addition of quotation marks around strings

Utilizing the npm module traverse to filter data from mongodb / mongoose has been quite helpful for me. Here is an example of the kind of data I might receive: [ { rating: 5, title: { da: 'Web udvikling', en: 'Web Development' } } ...

Send PHP form data seamlessly without reloading the page or using AJAX

I have searched extensively but could not find a solution to my problem. I am struggling to understand how to achieve this. My form consists of 2 textboxes and 1 submit button, named 'form1'. This is the code I have been using: <script type ...

Ways to exit the current browser window?

Is there a way to close the current browser window using JavaScript or another language that supports this functionality? I've tried using the window.close() function, but it seems to only work for windows opened with window.open(). Thank you in adv ...

Transform the text column into JSON format

We currently have a resource bundle/properties formatted as follows: [tag1] server1 server2 [tag2] server3 server4 [tag3] server5 server6 [No Software Installed] server7 [tag2] server8 [tag5] server9 [tag1] server10 server11 [tag3] server12 server13 serve ...

Error: The locator I used with the ID getUserById did not find any elements

Here is the code snippet from my HTML file: <h1>User list</h1> <button class="btn btn-primary" [routerLink]="'/register'">Register</button> <br> <br> <table class="table& ...

What is the best approach for iterating through the creation of Objects?

Here is a simplified version of the current code, with fewer rows and properties. var row1 = new Object(); var row2 = new Object(); var row3 = new Object(); var row4 = new Object(); var row5 = new Object(); var row6 = new Object(); var row7 = new Object() ...

problems with using array.concat()

I am attempting to reverse a stream of data using a recursive call to concatenate a return array. The instructions for this problem are as follows: The incoming data needs to be reversed in segments that are 8 bits long. This means that the order of thes ...

I want to know how to move data (variables) between different HTML pages. I am currently implementing this using HTML and the Django framework

I am currently working on a code where I am fetching elements from a database and displaying them using a loop. When the user clicks on the buy button, I need to pass the specific product ID to another page. How can I retrieve the product ID and successful ...

Learn the technique for showcasing numerous markers on Google Maps, each equipped with its own individualized info windows

https://i.sstatic.net/1tTUD.png // map center var center = new google.maps.LatLng(2.855262784366583, 105.4302978515625); function initialize() { var mapOptions = { center: center, zoom: 7, mapTypeId: google.maps.MapTypeId.ROADMAP }; // Create a < ...

How can I trigger a PHP function by clicking a button on a PHP page that has already been loaded?

While I've come across a variety of examples, I haven't been able to make them work for the simple task I need to accomplish. The code in these examples seems overly complex compared to what I require. In essence, I have a form that processes dat ...

How can I exclude .map.js files in Angular 2?

Can someone help me figure out how to exclude all .js.map files in my Angular 2 project? I've tried various combinations in my .gitignore file, but none of them seem to be working. *.log typings src/app/**/*.js src/app/**/*.map node_modules .idea/ sr ...

What is the reason for the Client Height value not affecting other elements?

I'm trying to set the spacing of my sidebar from the top equal to the height of the header. However, when I use clientHeight in JavaScript to get the height and then try to apply it to other elements using marginTop or top values (with position includ ...

The hover effect is functional on the majority of browsers, with the exception of Safari and Chrome on a Mac computer

Within my html code, there are various tiles containing two images (one in jpg format as the background and one in png with transparency as the foreground) along with a hover effect: When hovering over a tile, the image zooms in towards the position of the ...

Tips for displaying an edit icon on an individual row item when it is hovered

Attempting to create a functionality where hovering over a row in React triggers the appearance of an edit button/icon next to the folder name. The current approach involves assigning individual states to each row and utilizing a key to track them separate ...

Tips on integrating JavaScript with embedded Ruby code

I am attempting to generate a flash message that displays after a user clicks a link in my js.erb file. $('a').click(function(){ <% flash.now[:alert]= "Successfully added "%> this.innerHTML <% "to cart" %> }) M ...

How can you pass an authorization token in a Next.js post request when using Django REST framework?

Is there a way to successfully pass a Django authorization token in Next.js using Axios? I attempted this method, but encountered a 404 error. let token = "Token 8736be9dba6ccb11208a536f3531bccc686cf88d" await axios.post(url,{ headers ...

Managing communication with a serial port via a web application (PHP, JavaScript) with the assistance of MySQL and Python

Looking for feedback on my current project. I am working on creating a PC application that can communicate with a serial port to send and receive data. The data received by the application can either be requested or unsolicited. To manage the serial por ...

Error message: "The property is not found within the specified type when using the OR operator with

Within my Angular component, I am faced with a challenge involving an Input that can be one of two types. @Input() profile: UserProfileDetails | BusinessProfileDetails; The structure of the profile template is straightforward and I want to avoid duplicati ...

Adjust the speed of the remaining divs below to move up when one is deleted using Ajax and jQuery

My div elements are arranged from top to bottom, and when one selected div is removed or faded out, all other divs below it shift up suddenly to fill the gap. While this behavior is functional, I would prefer to slow down the movement of the divs shifting ...