0

In my Vue app: I'm using v-for to render product divs, each of which with a pair of buttons, 'new' and 'used', and I want to add 'active' class when a button is clicked. Problem is that I'm adding 'active' class to either all 'new' buttons or all 'used' buttons; pretty sure there's a Vue way to do this, other than getting the index of the clicked button and setting class to corresponding el. What am I missing?

markup:

<div id="app">
    <div class="products">

        <div class="product" v-for="product in products" :key="product.productID">
            <div class="product-image">
                <img :src=product.imgDef :alt="product.name" v-bind:title="product.name" />
            </div>
            <div class="product-summary">
                <p>{{ product.name }}</p>
                <p>{{ product.info }}</p>
                <ul>
                    <li v-for="detail in product.details">{{ detail }}</li>
                </ul>

                <div
                    class="conditions"
                    v-for="variant in product.variants"
                    v-bind:key="variant.variantID"
                    v-bind:style="variant.variantStyleObj"
                >
                    <!--
                        renders a 'new' button and a 'used' button in each div.product;
                        - clicking should add 'active' class only to clicked button
                    -->
                    <button
                        type="button"
                        v-on:click="updateProductImage(variant.variantImage, variant.variantID, product.productID, variant.condition)"
                        :class="[ 'condition', variant.condition, {active: conditionClass == variant.condition} ]"
                    >
                    {{ variant.condition }}
                    </button>
                </div>
            </div><!-- END .product-summary -->
        </div><!-- END .product -->
</div><!-- END #app -->

js:

var app = new Vue({
    el: '#app',
    data: {
        products: [
            {
                productID: 1,
                name: "",
                info: "",
                imgDef: "../../images-dev/...jpg",
                details: [],
                inStock: false,
                variants: [
                    {
                        variantID: "123",
                        variantImage: "../../images-dev/1a.jpg",
                        condition: "new"
                    },
                    {
                        variantID: "321",
                        variantImage: "../../images-dev/1b.jpg",
                        condition: "used"
                    }
                ],
                inventory: 9
            },
        cart: 0,
        // conditionClass: ""
    },
    methods: {
        addToCart: function() {
            this.cart += 3;
        },
        updateProductImage: function(variantImage, variantKey, productKey, variantCondition) {
            this.products[productKey-1].imgDef = variantImage;
            this.conditionClass = variantCondition;
        }
    }
})
4
  • make a conditionClass for every variants and change that instead of the global conditionClass Commented Mar 30, 2018 at 18:01
  • Thanks @JulioGuerra. How would you solve this if you couldn't touch the data structure? Commented Mar 30, 2018 at 18:35
  • It's going to be possible to active multiple buttons at the same time ? Commented Mar 30, 2018 at 19:26
  • no, only one active button in the view Commented Mar 30, 2018 at 19:42

1 Answer 1

1

If you don't want to change products array, you can, instead of using a single global conditionClass:

var app = new Vue({
    el: '#app',
    data: {
        // ...
        cart: 0,
        conditionClass: ""
    },

Make it into an object, in a way that it will work as a map that will have the productID as keys and the product's conditionClass.

Demo JSFiddle here.

Example:

var app = new Vue({
    el: '#app',
    data: {
        // ...
        cart: 0,
        conditionClass: {} // initialize as empty object
    },

Change the :class to access conditionClass via a key (stripped other parts for brevity):

From

<button ... :class="[{active: conditionClass == variant.condition}]">

To:

<button ... :class="[{active: conditionClass[product.productID] == variant.condition}]">

And, finally, set the key in the method:

updateProductImage: function(variantImage, variantKey, productKey, variantCondition) {
  this.products[productKey - 1].imgDef = variantImage;
  // line below was: this.conditionClass = variantCondition;
  Vue.set(this.conditionClass, productKey, variantCondition);
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you @acdcjunior. I noticed in your Fiddle that your solution allows either 'new' or 'used' left active per product; though my intention was to have only a single active button in the view, your version is perhaps a useful feature. I'll have a go implementing your approach shortly, cheers.
Ah, if you want only one active for the whole app, use, instead, in the last line of that method: Vue.set(this, 'conditionClass', {[productKey]: variantCondition});
Thanks again @acdcjunior, for your solution and for leading me to read up on Vue.set

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.