What is the best way to apply a specific style based on the book ID or card ID when a click event occurs on a specific card in vue.js

My latest project involves creating a page that displays a variety of books, with the data being fetched from a backend API and presented as cards.

Each book card features two button sections: the first section includes "ADD TO BAG" and "WISHLIST" buttons (initially visible), while the second section contains a "ADDED TO BAG" button (initially hidden).

When a user clicks on the "ADD TO BAG" button on a specific card, it should hide both the "ADD TO BAG" and "WISHLIST" buttons and display the "ADDED TO BAG" button exclusively for that clicked card. I need assistance in applying styles only to the clicked card, while the remaining cards should remain unaffected.

This is how the default page looks before clicking on the "ADD TO BAG" button:

After clicking on the "ADD TO BAG" button, this is the output I currently get:

DisplayBooks.vue

<template>
  <div class="carddisplay-section">
      <div v-for="book in books" :key="book.id" class="card book">
          <div class="image-section">
              <div class="image-container">
                  <img  v-bind:src="book.file" />
              </div>
          </div>
          <div class="title-section">
              {{book.name}}
          </div>
          <div class="author-section">
              by {{book.author}}
          </div>
          <div class="price-section">
              Rs. {{book.price}}<label class="default">(2000)</label>
              <button v-if="flag" class="btn-grp" type="submit" @click="handlesubmit();Togglebtn();">close</button>
          </div>
          <div class="buttons">
  <!-- This is my button section -->            
  <div class="button-groups">
                  
                  <button type="button"  @click="toggle(book.id);toggleClass(book.id);" v-bind:class="[storeBooks.indexOf(book.id) >-1 ? 'red' : 'blue']" class="AddBag">Add to Bag</button>
                  
                  <button  class="wishlist">wishlist</button>
              </div>
              <!-- v-if="state==false" -->
              <div v-bind:class="[!(storeBooks.indexOf(book.id)) >-1 ? 'blue':'red']" @click="toggle(book.id)" class="AddedBag">
                  <button class="big-btn">Added to Bag</button>
              </div>
          </div>
      </div>
  <!-- <Cart :cardId="clickedCard" v-if="false" /> -->
  </div>
  </template>
  
  <script>
  import service from '../service/User'
  export default {
      data() {
          return {
              isActive:true,
              storeBooks:[],
              result: 0,
              authorPrefix: 'by',
              pricePrefix: 'Rs.',
              defaultStrikePrice: '(2000)',
              buttonValue: 'close',
              flag: true,
              state: true,
              clickedCard: '',
              books: [{
                  id: 0,
                  file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
                  name: 'Dont Make me think',
                  author: 'Sai',
                  price: '1500'
              }, ]
          }
      },
      methods: {
          toggleClass: function(event){
              this.isActive = !this.isActive;
              return event;
          },
          toggle (id) {
            this.clickedCard = id;
            const index = this.storeBooks.indexOf(id);
            if(index > -1) {this.storeBooks = this.storeBooks.splice(index,1)}
            else{
              this.storeBooks.add(id)
            }},
          flip() {
              this.state = !this.state;
          },
          Togglebtn() {
              this.flag = !this.flag;
          },
          handlesubmit() {
              service.userDisplayBooks().then(response => {
                  this.books.push(...response.data);     
              })
          },
      }
  }
  </script>
  
  <style lang="scss" scoped>
      @import "@/styles/DisplayBooks.scss";
  </style>

Following the update to the component code, here is the current output: However, my expected outcome can be seen here: [what I need is]3

DisplayBooks.scss

@import "colors";
.carddisplay-section {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    align-content: space-around;
    gap: 10px;
}
.card:hover{
    box-shadow:0.6px 0.6px 0.6px 0.6px rgb(173, 206, 206);
}
.card {
    margin-top: 55px;
    margin-left: 110px;
    background:$pink;
    width: 235px;
    height: 315px;
    background: $pale_white 0% 0% no-repeat padding-box;
    border: 1px solid $border_clr;
    border-radius: 3px;
    opacity: 1;
}

.image-section {
    width: 233px;
    height: 172px;
    background: #F5F5F5 0% 0% no-repeat padding-box;
    border-radius: 2px 2px 0px 0px;
    opacity: 1;
}

img{
    margin-left: 67px;
    margin-top: 17px;
    width: 105px;
    height: 135px;
    opacity: 1;
    border:none;
}

.title-section {
    text-align: left;
    font: normal normal normal 14px/19px Roboto;
    letter-spacing: 0.2px;
    color: $light_black;
    opacity: 1;
    margin-left: 20px;
    margin-top: 3px;
    width: 130px;
    height: 19px;
    text-transform: capitalize;
}

.author-section {
    text-align: left;
    font: normal normal normal 13px/13px Roboto;
    letter-spacing: 0px;
    color: $light_grey;
    opacity: 1;
    width: 123px;
    height: 13px;
    margin-left: 20px;
    margin-top: 7px;
}

.price-section {
    text-align: left;
    font: normal normal bold 12px/16px Roboto;
    letter-spacing: 0px;
    color: $light_black;
    opacity: 1;
    margin-left: 20px;
    height: 16px;
    margin-top: 26px;
    display: flex;
    justify-content: flex-start;

}

label {
    text-decoration-line: line-through;
    font: normal normal normal 10px/13px Roboto;
    letter-spacing: 0px;
    color: $light_grey;
    opacity: 1;
    width: 36px;
    height: 13px;
    margin-top: 2.5px;
    margin-left: 1em;
}

button[type="submit"] {
    border: none;
    padding-left: 65px;
    background: none;
    font-size: 15;
}
.button-groups{
    display:flex;
    margin-top:8px;
}
.AddBag{
    background: #A03037 0% 0% no-repeat padding-box;
    border-radius: 2px;
    opacity: 1;
    width: 93px;
    height: 29px;
    margin-left:20px;
    color: #FFFFFF;
    text-transform: uppercase;
    opacity: 1;
    font-size: small;
}
.wishlist{
    margin-left:4px;
    color: #FFFFFF;
    text-transform: uppercase;
    opacity: 1;
    font-size: small;
    border: 1px solid #7c7a7a;
    border-radius: 2px;
    opacity: 1;
    color: #0A0102;
    width:93px;
}
.big-btn{
    width: 191px;
height: 29px;
margin-left:20px;
background: #3371B5 0% 0% no-repeat padding-box;
border-radius: 2px;
opacity: 1;
color:#FFFFFF;
}
.red{
    background: red;
}   
.blue{
    background: yellow;
    display:none;
}

Answer №1

To keep track of which card is added, you can create an array in the component's data function called `storeBooks`, like this: storeBooks=[]

toggle = (id) => {
 const index = this.storeBooks.indexOf(id);
 if(index > -1) {this.storeBooks = this.storeBooks.splice(index,1)}
 else{
  this.storeBooks.add(id)
 }}

In the book card section, you can use

isActive = storeBooks.indexOf(book.id) > -1
to set a class accordingly.

Here's an example:

<template>
  <div class="carddisplay-section">
      <div v-for="book in books" :key="book.id" class="card book">
          <div class="image-section">
              <div class="image-container">
                  <img  v-bind:src="book.file" />
              </div>
          </div>
          <div class="title-section">
              {{book.name}}
          </div>
          <div class="author-section">
              by {{book.author}}
          </div>
          <div class="price-section">
              Rs. {{book.price}}<label class="default">(2000)</label>
              <button v-if="flag" class="btn-grp" type="submit" @click="handlesubmit();Togglebtn();">close</button>
          </div>
          <div class="buttons">
  <!-- This is my button section -->            
  <div class="button-groups">
                  
                  <button type="button"  @click="toggle(book.id);toggleClass(book.id);" v-bind:class="[storeBooks.indexOf(book.id) >-1 ? 'red' : 'blue']" class="AddBag">Add to Bag</button>
                  
                  <button  class="wishlist">wishlist</button>
              </div>
              <!-- v-if="state==false" -->
              <div v-bind:class="[storeBooks.indexOf(book.id) >-1 ? 'blue':'red']" @click="toggle(book.id)" class="AddedBag">
                  <button class="big-btn">Added to Bag</button>
              </div>
          </div>
      </div>
  <!-- <Cart :cardId="clickedCard" v-if="false" /> -->
  </div>
  </template>
  
  <script>
  import service from '../service/User'
  export default {
      data() {
          return {
              isActive:true,
              storeBooks:[],
              result: 0,
              authorPrefix: 'by',
              pricePrefix: 'Rs.',
              defaultStrikePrice: '(2000)',
              buttonValue: 'close',
              flag: true,
              state: true,
              clickedCard: '',
              books: [{
                  id: 0,
                  file: 'https://images-na.ssl-images-amazon.com/images/I/41MdP5Tn0wL._SX258_BO1,204,203,200_.jpg',
                  name: 'Dont Make me think',
                  author: 'Sai',
                  price: '1500'
              }, ]
          }
      },
      methods: {
          toggleClass: function(event){
              this.isActive = !this.isActive;
              return event;
          },
          toggle (id) {
            this.clickedCard = id;
            const index = this.storeBooks.indexOf(id);
            if(index > -1) {this.storeBooks = this.storeBooks.splice(index,1)}
            else{
              this.storeBooks.add(id)
            }},
          flip() {
              this.state = !this.state;
          },
          Togglebtn() {
              this.flag = !this.flag;
          },
          handlesubmit() {
              service.userDisplayBooks().then(response => {
                  this.books.push(...response.data);     
              })
          },
      }
  }
  </script>
  
  <style lang="scss" scoped>
      @import "@/styles/DisplayBooks.scss";
  </style>

Answer №2

If you're currently using just one boolean to check if a book has been added to the cart, it may be affecting all your other books when triggered. To avoid this issue, consider adding an extra key to your books data to track individual book statuses. You can achieve this by utilizing the spread operator and map function on arrays:

data  = data.map(item => ({...item, isAdded: false}))

This snippet of code will add an "isAdded" key with a value of false to each book object in your data. You can then use this new key in your HTML code with v-show to control which buttons are displayed. Here's an example:

<div class="button-groups" v-show="!book.isAdded">
  <button type="button" @click="book.isAdded=!book.isAdded;" class="AddBag red"&bt;Add to Bag</button>
  <button  class="wishlist">wishlist</button>
</div>

<div @click="book.isAdded=!book.isAdded" class="AddedBag blue" v-show="book.isAdded">
  <button class="big-btn">Added to Bag</button>
</div>

By using the "isAdded" variable in this way, you have better control over displaying the correct buttons. It's also recommended to choose a clearer name for the variable to improve understanding.

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

Transferring data between JavaScript and PHP

Is there a way to transfer data from JavaScript to PHP when a button is clicked? For instance, can the getdate() function's result be sent to PHP? This transferred value will then be utilized for database manipulation. ...

Transforming the input button into images

I'm new to JavaScript and I'm looking to change the show button and hide button to images instead. The show button image should be different from the hide button image. Can anyone guide me on how to achieve this? Click here for reference $( ...

Creating NextJS Route with Dynamic Links for Main Page and Subpages

In my NextJS project, I have dynamic pages and dynamic subpages organized in the following folders/files structure: pages ├── [Formation] ├── index.js │ ├── [SubPage].js Within index.js (Formation Page), I create links like this: < ...

The methodology of executing a Javascript function

I have the following code snippet: (function() { $(document).ready(function() { //Event handlers $('.project-delete').on('click', function() { deleteProject($(this)); }); $('.keyword-delete').on(&ap ...

Creating TypeScript modules for npm

I have been working on creating my first npm module. In the past, when I used TypeScript, I encountered a challenge where many modules lacked definition files. This led me to the decision of developing my module in TypeScript. However, I am struggling to ...

Is there a way to convince Python to interpret data as a multi-dimensional list instead of a string when converting from HTML?

Currently, I am working on formatting table data in HTML and then sending it to Python using the script below: var html_table_data = ""; var bRowStarted = true; var count1 = 1 $('#woTable tbody>tr').each(function () { if (count1 != 1) { ...

Why isn't the Full Calendar loading automatically within a Bootstrap Tab?

I have been working on a travel website and incorporated a bootstrap tab feature. In the first tab, I have some content, while in the second tab, I've added a full calendar with JavaScript. Everything seems to be functioning correctly when the full ca ...

Having trouble interacting with the "Continue" button on PayPal while using Selenium

Recently, I have encountered an issue with automating payments via PayPal Sandbox. Everything used to work smoothly, but now I am unable to click the final Continue button no matter what method I try. I have attempted regular clicks, using the Actions cl ...

angularJS controller does not seem to be functioning properly due to a certain

var today = new Date(); var dd = today.getDate(); var mm = today.getMonth() + 1; //January is 0! var yyyy = today.getFullYear(); var currentdate = yyyy + ',' + mm + ',' + dd; var appointmentDate = moment($scope.AppointmentsList[i].J).f ...

Leveraging a variable in Python for XPATH in Selenium

I have a variable that looks like this: client_Id = driver.execute_script("return getCurrentClientId()") I want to update the XPATH by replacing the last value (after clientid=2227885) with the client_Id variable. So: prog_note = wait.until(EC.p ...

What could be causing the images to not display on my HTML page?

My program is designed to display an image based on the result of the random function. Here is my HTML: <div> <h2>Player 0:</h2> <div id="MainPlayer0"></div> </div> Next, in my TypeScript fi ...

Firefox and Internet Explorer have a tendency to break lines at very specific pixel measurements

Hey, I have a situation that goes like this: I have 4 objects with dimensions of exactly 76x76px placed within a 320px container. This setup seems to work well in Chrome as seen above. However, in Firefox and IE9, it looks like this: even though the < ...

Getting latitude and longitude from Google Maps in a React Native appHere are the steps to

Looking to retrieve the latitude and longitude from Google using React Native. As a newcomer to React Native, I have been unable to find any resources to address this issue. Any assistance would be greatly appreciated. Thanks! Thanks ...

Detecting Scroll on Window for Specific Element using Jquery

I need help troubleshooting my code. I am trying to activate only one item that comes from the bottom of the page, but instead all div elements are getting activated. $(window).scroll(function() { $('.parallax').each(function(e) { if($( ...

What is the process for implementing an image as the background instead of a color for each column in a Google chart?

I'm currently working with Google Chart and I have set up a column chart. However, I am looking to display a background image instead of a background color in each column. Is this achievable with Google Chart? If so, how can I accomplish this? If anyo ...

The jQuery focusout event is triggered on a form element, even if a child element is clicked within the form

How can I ensure that the focusout event is triggered only when the user is not focusing on the entire form element? Currently, if the user clicks on any of the form's inner elements such as the text field or the button, the event is still being trigg ...

Utilizing Typescript to Inject Generics and Retrieve the Name of an ES6 Module

I am currently working on developing a versatile repository using: Typescript ES6 Angular 1.x However, I am facing challenges in determining the correct way to inject the Entity and retrieve its module name. The main reason for needing the name: I adh ...

Why won't my setTimeout function work?

I'm having trouble working with the setTimeout function, as it doesn't seem to be functioning properly. First and foremost, Player.prototype.playByUrl = function (url) { this.object.data = url; return this.play(); } The co ...

Express 4 app.use not functioning properly

My backend setup is fairly straightforward with a few routes. I prefer to keep the route logic separate from the server.js file, but for some reason, when I make a POST request to the route, it returns a 404 error. In the server.js file: // Import necess ...

The function Map() in React-Leaflet cannot be executed, despite the presence of data

I am attempting to replicate the React-Leaflet example of a list of markers. I have an array of objects that I am passing to MarkerList to be transformed into Fragments and displayed on the map. However, my mapping function is not functioning as expected ...