Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

V-model does not get updated after checkbox clicked

Tags:

vue.js

Any idea how to resolve this problem:

in this example, the author uses vue 2.3.2 which works perfect,

new Vue({
  el: '#app',
  data: {
    users: [{
        "id": "Shad",
        "name": "Shad"
      },
      {
        "id": "Duane",
        "name": "Duane"
      },
      {
        "id": "Myah",
        "name": "Myah"
      },
      {
        "id": "Kamron",
        "name": "Kamron"
      },
      {
        "id": "Brendon",
        "name": "Brendon"
      }
    ],
    selected: [],
    allSelected: false,
    userIds: []
  },
  methods: {
    selectAll: function() {
      this.userIds = [];

      if (this.allSelected) {
        for (user in this.users) {
          this.userIds.push(this.users[user].id.toString());
        }
      }
    },
    select: function() {
      this.allSelected = false;
    }
  }
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>

<div id="app">
  <h4>User</h4>
  <div>
    <table>
      <tr>
        <th>Name</th>
        <th>Select All<input type="checkbox" @click="selectAll" v-model="allSelected"></th>
      </tr>
      <tr v-for="user in users">
        <td>{{ user.name }}</td>
        <td><input type="checkbox" v-model="userIds" @click="select" :value="user.id"></td>
      </tr>
    </table>
  </div>

  <span>Selected Ids: {{ userIds }}</span>
</div>

when I switch it to 2.5.16 ( <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> ) , the behavior is wierd:

When click the selectAll checkbox, only that checkbox checked, but when I toggle it to uncheck, all the checkboses below get checked

enter image description here

enter image description here

enter image description here

like image 465
Kuan Avatar asked Jul 19 '18 18:07

Kuan


2 Answers

For consistent browser functionality, I can recommended to not use click/change on checkboxes. Instead, bind the checkbox to a value (which you've already done), and then use a watcher on the value. This way, the real value of the checkbox will always accurately represent it's state. So you'd have something like this:

<input type="checkbox" v-model="allSelected">


Vue.component({..., {
    data: function() {
             return {
                allSelected: false,
             }
          }
    },
    watch: {
        allSelected: function(val){
            //Use your source of truth to trigger events!
            this.doThingWithRealValue(val); 
        }
    }
});

You're already using your component data value of allSelected as the source of truth, so you should use this source of truth as the real triggering element value, not a click. Whenever the value of allSelected changes, your code will get ran. This solves the problem without the rendering order weirdness.

like image 51
rob Avatar answered Nov 11 '22 08:11

rob


As pointed out by rob in the comments and in his answer you cannot rely on @click / @input / @change to have the same behaviour in all browsers in regards to their execution order relative to the actual model change. There is an issue at the VueJS repository with a bit more context: https://github.com/vuejs/vue/issues/6709

The better solution is to watch the model for changes and then react accordingly.

new Vue({
  el: '#app',
  data: {
    users: [{
        "id": "Shad",
        "name": "Shad"
      },
      {
        "id": "Duane",
        "name": "Duane"
      },
      {
        "id": "Myah",
        "name": "Myah"
      },
      {
        "id": "Kamron",
        "name": "Kamron"
      },
      {
        "id": "Brendon",
        "name": "Brendon"
      }
    ],
    selected: [],
    allSelected: false,
    userIds: []
  },
  methods: {
    selectAll: function() {
      this.userIds = [];

      if (this.allSelected) {
        for (user in this.users) {
          this.userIds.push(this.users[user].id.toString());
        }
      }
    },
    select: function() {
      this.allSelected = false;
    }
  },
  watch: {
      allSelected: function () {
          this.selectAll()
      }
  }
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

<div id="app">
  <h4>User</h4>
  <div>
    <table>
      <tr>
        <th>Name</th>
        <th>Select All<input type="checkbox" v-model="allSelected"></th>
      </tr>
      <tr v-for="user in users">
        <td>{{ user.name }}</td>
        <td><input type="checkbox" v-model="userIds" @click="select" :value="user.id"></td>
      </tr>
    </table>
  </div>

  <span>Selected Ids: {{ userIds }}</span>
</div>
like image 45
puelo Avatar answered Nov 11 '22 10:11

puelo