Reputation: 11389
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
Upvotes: 11
Views: 20563
Reputation: 6056
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>
Upvotes: 4
Reputation: 2144
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.
Upvotes: 12