Is there a way to apply toggling and styles to only the card that was clicked in Vue.js?

DisplayBooks.vue consists of a single page responsible for showcasing books fetched from a backend API. Each card on the UI features two buttons - ADD TO BAG and ADDED TO BAG. When a user clicks on the ADD TO BAG button of a specific card, it should toggle to display the ADDED TO BAG button exclusively on that card. I need assistance in implementing this functionality.

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">
            <div class="button-groups">
                <button type="button"  class="AddBag">Add to Bag</button>
                
            </div>
            <div  class="AddedBag">
                <button class="big-btn">Added to Bag</button>
            </div>
        </div>
    </div>

</div>
</template>

<script>
import service from '../service/User'

export default {
    data() {
       
        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: {
    
    
        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>

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: 275px;
    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;
}
   

Answer №1

To enhance the functionality, introduce a new array to store the book IDs added when the user clicks on the ADD To BAG button. Utilize v-if/v-else for conditional rendering:

    data() {
        return {
           
            flag: true,
            state: true,
            clickedCard: '',
            addedBooks:[],
            books: [....]
        }
    },

Incorporate this logic within the template:

    <div class="buttons">
            <div class="button-groups" v-if="!addedBooks.includes(book.id)">
                <button type="button"  @click="addedBooks.push(book.id)"  class="AddBag">Add to Bag</button>
                
            </div>
            <div  class="AddedBag" v-else>
                <button class="big-btn" @click="addedBooks=addedBooks.filter(id=>id!==book.id)">Added to Bag</button>
            </div>
        </div>

Example

new Vue({
  el: "#app",

  data() {
    return {

      flag: true,
      state: true,
      clickedCard: '',
      addedBooks: [],
      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'
        },
        {
          id: 1,
          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 {


    flip() {
      this.state = !this.state;
    },
    Togglebtn() {
      this.flag = !this.flag;
    },

  }
})
.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: 275px;
  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;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <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="">close</button>
      </div>
      <div class="buttons">
        <div class="button-groups" v-if="!addedBooks.includes(book.id)">
          <button type="button" @click="addedBooks.push(book.id)" class="AddBag">Add to Bag</button>

        </div>
        <div class="AddedBag" v-else>
          <button class="big-btn" @click="addedBooks=addedBooks.filter(id=>id!==book.id)">Added to Bag</button>
        </div>
      </div>
    </div>

  </div>
</div>

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

Can a CSS Grid layout be achieved without prior knowledge of image sizes?

I am working on a project where I am pulling images from Reddit and aiming to showcase them in a gallery-style grid. I have tried using a flex display and resizing the images to match dimensions, but unfortunately not all images have the same dimensions ...

Seeking a regular expression to identify special characters appearing at the beginning of a string

I'm looking to update my current regex pattern to include special characters at the beginning of a string value. Here's what I have right now: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*()_+])[A-Za-z\d][A-Za-z\d!@#$%^&*()_+.]{ ...

Controller unable to update AngularJS view

As the title suggests... I'm facing a challenge with this code snippet: (function(angular) { 'use strict'; angular.module('app', []) .controller('ctrl', ['$scope', function($scope) { $scope.ini ...

Using Nextjs Image as the Background for Layout

I have a question regarding implementing a common background image for all pages in my app. Currently, I have the main page.js code that displays an image as the background using Next Image component. However, I would like this background to be present thr ...

Obtain the value from the controller of the amazing-rating component

Currently utilizing the amazing Rating AngularJS library available at https://github.com/bandraszyk/awesome-rating I am interested in understanding how to retrieve the selected value and store it in my controller. The $scope.rating is returning undefined ...

Run C# script with the assistance of .load jquery

I have searched extensively for similar posts, but none seem to address the specific question I have regarding my project. What I am attempting to do is load different pages (.aspx) in an iframe dynamically. However, instead of using an iframe, I want to r ...

Guide on sending information using ajax using a button within a form

I've been working on creating a form that utilizes HTML5 validations and integrates Ajax to show the status of mail sending. However, I'm facing an issue where clicking the send button does not initiate the expected action. Form HTML: <div c ...

What is the process for retrieving data through a server-side API request?

I have been working on setting up my API URL in my Node.js backend application. I initially passed the entire URL in my frontend AJAX call, but now I want to transition to a server-side API call to avoid exposing the URL. Since I am not very experienced wi ...

Accessing an Accurate and Customized Menu Selection from an Old-School .ASP Loop Area

I am currently facing an issue with the note section in my form. Users with permissions can add information to a specific record, including deleting or editing their own notes. However, when I implemented a Jquery Modal Menu for confirming deletions, I enc ...

Passing events value in Fullcalendar Plugin by utilizing a javascript function to format events in the Fullcalendar event format

Currently, I am utilizing the full calendar plugin to display data on a calendar from a workflow. The values needed for the events are obtained in a separate function and need to be passed as fullcalendar events. I am uncertain about how to retrieve these ...

What is the recommended value for the auto-incremented ID field when using the POST method in Node.js?

Currently, I am utilizing the mysql package for NodeJs in order to Post Data to a MySqlDB. Below is an example of my code: app.post('/countries', (req, res) => { const country = req.body.country; const city = req.body.city; const t ...

vue Error: Attempting to access properties of an undefined object (specifically, 'NormalModule')

Working on a small web app written in vue, I utilize two different machines for testing and development. Github serves as a convenient platform for me to seamlessly switch between the two. Previously, I encountered no issues while working on my macos mach ...

Display different content based on the item selected in a listbox using headless UI and Vue.js

We are encountering an issue with the Listbox component from HeadlessUI. The problem is that we are unable to display content based on the selected option in the listbox. Here is the code for the listbox: <Listbox v-model="selectedElement"> ...

Obtain user input from a form and assign it to a variable in a jQuery AJAX

How can I pass the value of an HTML Input Form to a jQuery AJAX call's URL as `amt` in `url: "http://localhost:8080/orderNo?amount=" + amt,`? The input value logs to the console when calling getAmtValue(), but I'm struggling to access the `amt` ...

Arranging hierarchical data in JavaScript

After populating the hierarchy data, it is structured as follows: Here is a screenshot: I am looking to sort this data. Currently, I have a flat data object for rendering purposes. For example, my rendering object looks like this: renderedobjects=[ {1,. ...

Tips for comparing array objects in two separate React states

Comparing object arrays in two different React states can be challenging. Here is an example of the state values: review1 = [{name: John, Title: Manager},{name: Peter, Title: Engineer}, {name: Serena, Title: Supervisor}] review2 = [{personName: John, isma ...

Using async/await in combination with Vuex and Feathers

Please review my code below. I am attempting to integrate Async Await into vuex. While everything is functioning properly, I would like to call another action after this one using async await. However, the expected result is not being achieved as the conso ...

An easy way to adjust the date format when linking a date in ng-model with md-datepicker

<md-input-container> <label>Scheduled Date</label> <md-datepicker ng-model="editVersionCtrl.selectedPlannedDate" ng-change="editVersionCtrl.checkPlannedDate()"> </md-datepicker> </md-input-container> ...

CSS - Retain z-index until the transition is complete

Looking at this basic grid layout: <html> <head> <style> .grid { display: grid; grid-gap: 5px; grid-template-columns: repeat(13, 3fr); } .box { position: relat ...

What is the proper way to bind CoreUI's CSwitch component?

I am trying to incorporate the CSwitch component into a DevExtreme data grid. While the DxSwitch component functions correctly, I am unable to get the CSwitch to work properly. It seems like I might be using the wrong binding. Could that be the issue? < ...