The variable assigned just before a difficult cycle. Value has changed on javascript. But the value on the dom side has changed by the end of the loop. Can not display loading div. How can i display the loading div, while the loop continues.
var vm = new Vue({
el: '#app',
data: {
loading: false,
},
methods: {
run() {
this.loading = true;
console.log(this.loading);
for (let i = 0; i < 1000000000; i++) {}
this.loading = false;
console.log(this.loading);
},
runWithAsync() {
vm.loading = true;
console.log(this.loading);
new Promise(function(resolve, reject) {
for (let i = 0; i < 1000000000; i++) {}
resolve(true);
}).then((result) => {
vm.loading = false;
console.log(this.loading);
})
},
runWithAsyncTimeout() {
this.loading = true;
console.log(this.loading);
setTimeout(function() {
new Promise(function(resolve, reject) {
for (let i = 0; i < 1000000000; i++) {}
resolve(true);
}).then((result) => {
this.loading = false;
console.log(this.loading);
})
}.bind(this), 100)
}
}
})
.loading {
width: 100%;
height: 100vh;
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<button @click="run">run</button>
<button @click="runWithAsync">runWithAsync</button>
<button @click="runWithAsyncTimeout">runWithAsyncTimeout</button>
<button @click="loading = !loading">change loading</button> {{ loading }}
<div class="loading" v-if="loading"></div>
</div>
After thinking about this for a while, i came up with a brillant solution in the form of an onRender function.
Vue has an asynchronous update mechanism which you can wait for after adjusting data using Vue.nextTick or vm.$nextTick (view-model instance function). But this executes the callback after the DOM has been changed but not rendered yet. If you then requestAnimationFrame you end up right before the next render. At this point requesting another animation frame will cause the code to run until before the next frame's render, so finally a render will happen:
// This is dumb.
const onRender = callback =>
Vue.nextTick(() =>
requestAnimationFrame(() =>
requestAnimationFrame(callback)));
var vm = new Vue({
el: '#app',
data: {
loading: false,
},
methods: {
run() {
this.loading = true;
console.log(this.loading);
onRender(() => {
for (let i = 0; i < 1000000000; i++) {}
this.loading = false;
console.log(this.loading);
});
}
}
})
.loading {
width: 100%;
height: 100vh;
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<button @click="run">run</button>
<button @click="loading = !loading">change loading</button> {{ loading }}
<div class="loading" v-if="loading"></div>
</div>
With async support:
// Look at them arrows!
const onRender = (callback = () => {}) =>
new Promise(res =>
Vue.nextTick(() =>
requestAnimationFrame(() =>
requestAnimationFrame(() => res(callback())))));
var vm = new Vue({
el: '#app',
data: {
loading: false,
},
methods: {
async run() {
this.loading = true;
console.log(this.loading);
await onRender();
for (let i = 0; i < 1000000000; i++) {}
this.loading = false;
console.log(this.loading);
}
}
})
.loading {
width: 100%;
height: 100vh;
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<button @click="run">run</button>
<button @click="loading = !loading">change loading</button> {{ loading }}
<div class="loading" v-if="loading"></div>
</div>
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