0

I've been struggling with something that seems so simple, but yet so hard to understand why it's not working. I have an array of objects. I render the objects using v-for and within each render is are two buttons; one toggles the other and vice versa. My issue is I can't seem to make the 'mdi-pencil' button toggle the 'mdi-floppy' button. I've checked the console and the data changes but the buttons don't seem to be reacting to the data change.

Here's a CodePen recreation of my code.

This is the template:

<template>
    <v-row>
        <v-col v-for="(card, index) in cards" :item="card" :key="index">
            <v-btn 
                v-if="edit[card._id] == false" 
                @click="editCard(card)" 
                icon color="success"
            >
                <v-icon>mdi-pencil</v-icon>
            </v-btn>

            <v-btn 
                v-else" 
                @click="saveEdit(card)" 
                icon 
                color="success"
            >
                <v-icon>mdi-floppy</v-icon>
            </v-btn>
        </v-col>
      </v-row>
</template>

This is the script:

<script>
export default {
    name: 'AccountSettings',
    data() {
        return {
            cards: [
                {
                    _id: 1,
                    name: 'Number 2'
                },
                {
                    _id: 2,
                    name: 'Number 2'
                },
                {
                    _id: 3,
                    name: 'Number 3'
                }
            ],
            edit: {}
        }
    },
    mounted() {
        if(this.cards.length > 0) {
            this.edit = {};

            //creates properties for 'edit' object based on the cards array length

            for(let i = 0; i < this.cards.length; i++) {
                this.edit[this.cards[i]._id] = false;
            }
        }
    },
    methods: {
        //this method will toggle on the 'mdi-floppy' button

        editCard(card) {
            this.edit[card._id] = true;
            console.log(this.edit[card._id]);
            console.log(this.edit);
        },

        //this method will toggle on the 'mdi-pencil' button

        saveEdit(card) {
            this.edit[card._id] = false;
            console.log(this.edit[card._id]);
            console.log(this.edit);
        }
    }
}
</script>

The reason why I chose to do this:

mounted() {
    if(this.cards.length > 0) {
        this.edit = {};

        for(let i = 0; i < this.cards.length; i++) {
            this.edit[this.cards[i]._id] = false;
        }
    }
}

is because each object, when rendered in v-for, needs to have their own unique variable for the v-if condition. If all of the rendered objects all used the same variable for their v-if condition, then all of the buttons would toggle at the same time, which isn't what I want.

Doing this (below) will just toggle all the buttons and that's not what I want. I want the buttons to toggle independently:

//script

data() {
    return {
        ...
        edit: false
    }
}

//template

<v-col v-for="(card, index) in cards" :item="card" :key="index">
    <v-btn 
        v-if="edit == false" 
        @click="edit = true" 
        icon color="success"
    >
        <v-icon>mdi-pencil</v-icon>
    </v-btn>

    <v-btn 
        v-else 
        @click="edit = false" 
        icon 
        color="success"
    >
        <v-icon>mdi-floppy</v-icon>
    </v-btn>
</v-col>

2 Answers 2

1

The issue here is reactivity. You can't just add arbitrary properties to objects without using Vue.set or they won't be reactive:

Your loop needs to use Vue.set():

for(let i = 0; i < this.cards.length; i++) {
  Vue.set(this.edit, this.cards[i]._id, false)
}

And your click methods needs to use Vue.set()

methods: {
  editCard(card) {
    Vue.set(this.edit, card._id, true)
  },
  saveEdit(card) {
    Vue.set(this.edit, card._id, false)
  }
}

You may also use this.$set within components as well.

Sign up to request clarification or add additional context in comments.

1 Comment

Ahhhhhh. I knew it was something simple, but yet I couldn't figure it out. Thanks; much appreciated!
0

You need to use Vue.$set to check changes inside your object:

 mounted() {
    if(this.cards.length > 0) {
      this.edit = {};
      
      for(let i = 0; i < this.cards.length; i++) {
        this.$set(this.edit, this.cards[i]._id, false )
      }
    }
  },
  methods: {
    editCard(card) {
      //this.edit[card._id] = true;
      this.$set(this.edit, card._id , true )
      console.log(this.edit[card._id]);
      console.log(this.edit);
    },
    saveEdit(card) {
      //this.edit[card._id] = false;
      this.$set(this.edit, card._id , false )
      console.log(this.edit[card._id]);
      console.log(this.edit);
    }
  }

Comments

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.