3

I have a code to show that after clicking button A, it's hidden and button B is displayed. But I tried clicking on A and nothing happens. Here's my code:

<template>
    <table>
      <thead>
        <tr>
          <th>Column A</th>
          <th>Column B</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item, index) in items" :key="item.id">
          <td>{{ item.columnA }}</td>
          <td>{{ item.columnB }}</td>
          <td>
            <span v-show="!editing[index]">
              <button class="icon-A" @click="() => editItem(index)">A</button>
            </span>
            <span v-show="editing[index]">
              <button class="icon-B" @click="() => saveItem(index)">B</button>
            </span>
          </td>
        </tr>
      </tbody>
    </table>
  </template>
  
  <script>
  export default {
    name: "QTCongTac",
    data() {
      return {
        items: [
          { id: 1, columnA: 'A1', columnB: 'B1' },
          { id: 2, columnA: 'A2', columnB: 'B2' },
          { id: 3, columnA: 'A3', columnB: 'B3' },
        ],
        editing: [],
      };
    },
    methods: {
      editItem(index) {
        this.editing[index] = true;
        console.log("editItem")
      },
      saveItem(index) {
        this.editing[index] = false;
        console.log("saveItem")
      },
    },
  };
  </script>

After clicking on button A, I want it to be hidden and the button B is displayed.

Lucia
  • 51
  • 1
  • Does this answer your question? [Vue not triggering changes inside v-if in template when using index](https://stackoverflow.com/questions/74854103/vue-not-triggering-changes-inside-v-if-in-template-when-using-index) – Neha Soni Mar 27 '23 at 08:39

1 Answers1

2

Vue cannot detect the changes to the array when you set at an index outside the initial length of the array. Particularly Vue 2 can be fickle with reactivity.

A simple fix is to just push the index (or the item) into the array, and then check with includes().

In your methods:

    methods: {
      editItem(item) {
        this.editing.push(item)
      },
      saveItem(item) {
        this.editing = this.editing.filter(i => i !== item)
      },
    },

In the template:

<span v-show="!editing.includes(item)">
  <button class="icon-A" @click="() => editItem(item)">A</button>
</span>
<span v-show="editing.includes(item)">
  <button class="icon-B" @click="() => saveItem(item)">B</button>
</span>

See it running in the snippet:

new Vue({
  el: '#app',
  data() {
    return {
        items: [
          { id: 1, columnA: 'A1', columnB: 'B1' },
          { id: 2, columnA: 'A2', columnB: 'B2' },
          { id: 3, columnA: 'A3', columnB: 'B3' },
        ],
        editing: [],
      };
  },
  methods: {
      editItem(item) {
        this.editing.push(item)
        console.log("editItem")
      },
      saveItem(item) {
        this.editing = this.editing.filter(i => i !== item)
        console.log("saveItem")
      },
    },
})
<div id="app">
  <table>
      <thead>
        <tr>
          <th>Column A</th>
          <th>Column B</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item, index) in items" :key="item.id">
          <td>{{ item.columnA }}</td>
          <td>{{ item.columnB }}</td>
          <td>
            <span v-show="!editing.includes(item)">
              <button class="icon-A" @click="() => editItem(item)">A</button>
            </span>
            <span v-show="editing.includes(item)">
              <button class="icon-B" @click="() => saveItem(item)">B</button>
            </span>
          </td>
        </tr>
      </tbody>
    </table>
    
    <div> Editing: {{editing}} </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.10/vue.min.js"></script>

If you want to avoid the overhead of include() in favor of a lookup, you can use an object and Vue.set()/Vue.delete():

  data() {
    return {
        ...
        editing: {},
      };
  },
  methods: {
      editItem(index, item) {
        Vue.set(this.editing, index, item)
      },
      saveItem(index, item) {
        Vue.delete(this.editing, index)
      },
    },
})

new Vue({
  el: '#app',
  data() {
    return {
        items: [
          { id: 1, columnA: 'A1', columnB: 'B1' },
          { id: 2, columnA: 'A2', columnB: 'B2' },
          { id: 3, columnA: 'A3', columnB: 'B3' },
        ],
        editing: {},
      };
  },
  methods: {
      editItem(index, item) {
        Vue.set(this.editing, index, item)
        console.log("editItem")
      },
      saveItem(index, item) {
        Vue.delete(this.editing, index)
        console.log("saveItem")
      },
    },
})
<div id="app">
  <table>
      <thead>
        <tr>
          <th>Column A</th>
          <th>Column B</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item, index) in items" :key="item.id">
          <td>{{ item.columnA }}</td>
          <td>{{ item.columnB }}</td>
          <td>
            <span v-show="!editing[index]">
              <button class="icon-A" @click="() => editItem(index, item)">A</button>
            </span>
            <span v-show="editing[index]">
              <button class="icon-B" @click="() => saveItem(index, item)">B</button>
            </span>
          </td>
        </tr>
      </tbody>
    </table>
    
    <div> Editing: {{editing}} </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.10/vue.min.js"></script>
Moritz Ringler
  • 9,772
  • 9
  • 21
  • 34