1
1
100R2019-09-02 13:52:06
Vue.js
100R, 2019-09-02 13:52:06

How can I tell if a product has been added to the cart?

There is a catalog component, a product module, and something remotely resembling a shopping cart. Product module - stores a list of all products, and the order module - Id of the purchased product and quantity. How to determine that the "Buy" button of a particular product has been clicked, display a message about this and make the button invalid (disabled)?

Catalog.vue

<template>
    <b-row>
        <b-col sm="6" lg="4" xl="3" v-for="product in products" :key="product.id">
            <div class="image" v-lazy:background-image="product.image">
                Видно если нажали "Купить".
            </div>
            <div class="d-flex justify-content-between">
                <div>
                    <p class="mb-0">{{product.name}}</p>
                    <p class="mb-0">{{product.price}} грн.</p>
                </div>
                <b-button class="align-self-center" variant="outline-dark" @click="buyProduct(product.id)">Купить</b-button>
            </div>
        </b-col>
    </b-row>
</template>

<script lang="javascript">
    import { BRow, BCol, BButton } from 'bootstrap-vue'
    import { mapState, mapActions } from 'vuex'

    export default {
        components: {
            BRow,
            BCol,
            BButton
        },
        computed: mapState({
            products: state => state.product.all
        }),
        methods: {
            ...mapActions('order', ['buyProduct'])
        }
    }
</script>

product.js

import axios from 'axios'

export default {
    namespaced: true,
    strict: true,

    state: {
        page: 1,
        all: []
    },

    mutations: {
        incrementPage: (state) => {
            state.page++
        },
        addToAllProducts: (state, products) => {
            state.all.push(...products)
        }
    },

    actions: {
        getFourProducts: (context, $state) => {
            axios.get('http://api.local/api/products', {
                params: {
                    page: context.state.page
                },
            }).then(({ data }) => {
                if (data.data.length) {
                    context.commit('incrementPage')
                    context.commit('addToAllProducts', data.data)
                    $state.loaded()
                } else {
                    $state.complete()
                }
            })
        }
    }
}

order.js

export default {
    namespaced: true,
    strict: true,

    state: {
        all: []
    },

    mutations: {
        addToOrder(state, id) {
            state.all.push({
                id,
                quantity: 1
            })
        }
    },

    actions: {
        buyProduct(context, id) {
            context.commit('addToOrder', id)
        }
    }
}

Answer the question

In order to leave comments, you need to log in

2 answer(s)
0
0xD34F, 2019-09-02
@100R

Let's add a property to the products elements that will indicate whether the product is present in the cart. To do this, we rewrite it as follows:

computed: mapState({
  products: state => state.product.all.map(n => ({
    ...n,
    isOrdered: state.order.all.some(m => m.id === n.id),
  })),
}),

That's it, you can block the button: . And show purchase notification: . If you don't want to change an already existing computed property, you can do one more thing:<b-button :disabled="product.isOrdered"
computed: {
  isOrdered() {
    return Object.fromEntries(this.$store.state.order.all.map(n => [ n.id, true ]));
  },
},

In the template, instead product.isOrderedof the previous version, there will be isOrdered[product.id].
UPD. Here's a demo for you . The code is slightly different from yours and from the one above in the answer, plus a router is added, but I guess you'll figure out what's what.

I
Igor, 2019-09-02
@IgorPI

<template>
  <b-row>
    <b-col sm="6" lg="4" xl="3" v-for="product in products" :key="product.id">
      <div class="image" v-lazy:background-image="product.image">
        Видно если нажали "Купить".
      </div>
      <div class="d-flex justify-content-between">
        <div>
          <p class="mb-0">{{product.name}}</p>
          <p class="mb-0">{{product.price}} грн.</p>
        </div>
        <b-button class="align-self-center" variant="outline-dark" @click="buyProduct(product)">Купить</b-button>
      </div>
    </b-col>
  </b-row>
</template>

<script lang="javascript">
  import { BRow, BCol, BButton } from 'bootstrap-vue'
  import { mapState, mapActions } from 'vuex'

  export default {
    components: {
      BRow,
      BCol,
      BButton
    },
    computed: mapState({
      products: state => state.product.all
    }),
    methods: {
      buyProduct(product){
        console.log(product)
      }
    }
  }
</script>

As for the button, you can
<b-button class="align-self-center" variant="outline-dark" @click.once="buyProduct(product.id)">Купить</b-button>

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question