When you click on the checkbox, it visually changes by adding a tick symbol (v
).
After the checkbox is ticked, it triggers the change
event.
It is important to note that the Vue's checked
property does not get updated immediately. The update occurs when the change
event is handled and the checked
property is set accordingly. This process is illustrated in line 4 of the code snippet below:
methods: { // 1
change(isChecked) { // 2
this.count++; // 3
this.checked = isChecked // 4
When the value of this.count
is even and the setTimeout
function is not used, the this.checked
is immediately set back to its previous value.
Vue recognizes that this.checked
did not change after the method execution, so it does not update or repaint the component. This results in the tick symbol (v
) staying visible, even though it should not be. This is because Vue did not repaint the component.
You can test this behavior in the demo below. The updated
lifecycle method does not execute as expected:
let myCheckbox = Vue.component('my-checkbox', {
template:
`<div>
<input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}
</div>`,
props: ['checked'],
methods: {
change() {
this.$emit('change', event.target.checked);
}
},
// not executed because `checked` never changes (is always true)
updated() { console.log('my-checkbox updated') }
})
new Vue({
el: '#app',
data: {
checked: true,
count: 1
},
methods: {
change(isChecked) {
this.count++;
this.checked = isChecked
if (this.count % 2 === 0) {
this.checked = !isChecked
}
}
},
components: {
myCheckbox
}
})
span {
background: pink;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<my-checkbox :checked="checked" @change="change"></my-checkbox>
When
<span>{{count}}</span> is even your check will fail. - checked: <span>{{checked}}</span> - it never changes, always <b>true</b>, so my-checkbox <b>never</b> has to be updated/repainted.
</div>
Using the setTimeout
function, the this.checked
actually does change once the change(isChecked)
method finishes executing. This change triggers the repaint of the component.
However, shortly after the setTimeout
handler executes, resetting this.checked
back to its original value triggers another repaint. In the demo below, you can see the updated
hook being executed twice when this.count
is even.
It is worth noting that the recommended approach in Vue is to use Vue.nextTick()
instead of setTimeout
:
let myCheckbox = Vue.component('my-checkbox', {
template:
`<div>
<input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}
</div>`,
props: ['checked'],
methods: {
change() {
this.$emit('change', event.target.checked);
}
},
updated() { console.log('my-checkbox updated') },
})
new Vue({
el: '#app',
data: {
checked: true,
count: 1
},
methods: {
change(isChecked) {
this.count++;
this.checked = isChecked
if (this.count % 2 === 0) {
Vue.nextTick(() => {
this.checked = !isChecked
});
//setTimeout(() => {
// this.checked = !isChecked
//}, 10);
}
}
},
components: {
myCheckbox
}
})
span {
background: pink;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<my-checkbox :checked="checked" @change="change"></my-checkbox>
When
<span>{{count}}</span> is even the checked will be overridden. checked: <span>{{checked}}</span>
</div>