22

I wanted to make a component which gets text-box when mouse is over the image.

Below is HTML template.

<section class="item-container" v-for="(item, index) in items">
  <div class="image-box" @mouseenter="changeStatus(index)">
    <img class="image" src="item.link" alt>
  </div>
  <div class="text-box" @mouseleave="changeStatus(index)" v-if="show[index]">
    <h4>{{ item.name }}</h4>
    <p>{{ item.content }}</p>
  </div>
</section>

And below is app.js

new Vue({
  el: '#app',
  data: {
    show: [false, false, false],
    items: [
      {
        name: 'Author 1',
        content: 'Content 1'
      },
      {
        name: 'Author 2',
        content: 'Content 2'
      },
      {
        name: 'Author 3',
        content: 'Content 3'
      }
    ]
  },
  methods: {
    changeStatus: function(index) {
      this.show[index] = !this.show[index];
      console.log(this.show); 
      console.log(this.show[index]);  // console gets it as expected
    }
  }
});

When I execute above codes, I can find that the show property has changed. However, v-if is not updated. Can't we use array[index] for v-if or is there other reason for it?

1
  • Are you opposed to having boolean values on each item instead of a separate show array? Commented Jan 11, 2017 at 0:10

3 Answers 3

37

The problem is not about v-if, it's because Vue cannot detect the changes of an array element directly, this is one of the limitation of JavaScript.

Thus, Vue provides some helper functions for this, like Vue.set

Change this this.show[index] = !this.show[index]

to Vue.set(this.show, index, !this.show[index])

then it should work.

Vue.set is not the only solution, there are several ways to accomplish this, in case you may want to know.

You can use native methods of JavaScript array, Vue will hook on these methods so it can detect the changes.

Here is the example of the usage of .splice

this.show.splice(index, 1, !this.show[index])

Another way is to replace the array entirely. If you are using ES6, you can use the spread operator to clone the array easily:

this.show[index] = !this.show[index]
this.show = [...this.show]

.map will also work because it returns a new array

this.show = this.show.map((el, i) =>
  i === index ? !el : el
)
Sign up to request clarification or add additional context in comments.

5 Comments

this is neat, and Im still looking at the other solutions you listed but.. vue.set does not work. -- example: jsfiddle.net/qnq2munr/1
@robertotomás You are using Vue 1.0, there are some differences in the template syntax. Also don't use minified js during development, or you can't see the error messages.
I had this exact same problem and this was really useful. Thanks very much ;)
Uisng Vue.set in both the switch part AND the initialisation in created did work.
Actually the above is incorrect. I just had to change the initialsation to use Vue.set, not the changeStatus function (or in my case the seeMe function)
3

You can use a JS object instead of an array and get the same effect. In other words, replace show: [false, false, false], with show: {0:false, 1:false, 2:false},.

2 Comments

Thanks a ton, I've dealt with this issue on and off for 6 months, this made instant sense to me and worked instantly, thank you.
not working here, even with this object approach =(
3

In component in methods you can use:

this.$set(this.show, index, !this.show[index])

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.