When I have a Vue component in a .vue file with a data member isLoading: false
, and a template:
<div v-show="isLoading" id="hey" ref="hey">Loading...</div>
<button @click="loadIt()">Load it</button>
And a method:
loadIt() {
this.isLoading = true
this.$nextTick(() => {
console.log(this.$refs.hey)
// ...other work here that causes other DOM changes
this.isLoading = false
})
}
("Loading" here refers to loading from the in-memory store, not an AJAX request. I want to do this so that I can show a simple "loading" indicator instantly while the DOM changes that might take 0.2-0.5 second or so are occurring.)
I thought that the $nextTick
function would allow both the virtual and actual DOM to update. The console log reads that the item was "shown" (removing the display: none
style). However, in both Chrome and Firefox, I never see the "Loading..." indicator; the short delay happens, and the other DOM changes happen without the Loading indicator being shown.
If I use setTimeout
instead of $nextTick
, I will see the loading indicator, but only when the other work is sufficiently slow. If there is a delay of a few tenths of a second, the loading indicator never shows. I'd like it to appear immediately on click so that I can present a snappy GUI.
Example in a fiddle
According to this Github 'issue' there is some 'funky' business going on with Vue.nextTick
. It appears that the nextTick
function actually fires just before the DOM is about to re-render. nextTick
will be fired before the DOM can show the loading
div and immediately gets hidden once nextTick
finishes. There are some suggetions in the Github issue but not all of them work.
The only way they got it to work is if you use setTimeout
with a delay. Setting the delay to 1 millisecond won't always guarantee a DOM update, so it is suggested to use around 25 milliseconds. I know this isn't really what you wanted but it's all I could find. Hopefully you get some use out of the Github link.
JSFiddle with examples
I'm pretty sure you're over complicating things.
This appears to work just fine:
new Vue({
el: "#app",
data: {
isLoading: false,
done: false
},
methods: {
loadIt() {
this.isLoading = true
// Simulate some long running AJAX request...
window.setTimeout(() => {
this.done = true
this.isLoading = false
}, 3000)
}
}
})
Edit: After reading further about your issue, the code below appeared to work for me. It's kind of a hack really. I guess your issue is to do with the event loop?
new Vue({
el: "#app",
data: {
isLoading: false,
done: false
},
methods: {
loadIt () {
this.isLoading = true
window.setTimeout(() => {
for (var i = 1; i < 40000000; ++i) {
this.done = !i
}
this.done = true
this.isLoading = false
}, 10)
}
}
})
Note: This 100% works but I did notice that JSFiddle is a bit odd with 'saving' and 'running'. Make sure to hit Command / Ctrl + S first and then click the Run button.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With