1

I have a small web application written in Vue.js and I am trying to show a loading indicator on the page while a list of items is being filtered and re-rendered. The list is not fetched asynchronously, it is just a list of items from a computed property, based on some filter fields and is updated as the filters are changed. There is a delay of a few seconds before the list is updated in the UI because the list has about 1400 items and each item is a card-style component with an image, a button, icons, and some text. I have attempted to show a "Loading..." message between filter changes and new list rendering, but can't seem to get it to show to the user.

I have put together a simplified fiddle as an example: https://jsfiddle.net/zm4z9657/1/

EDIT: Here is the code snippet:

new Vue({ el: '#app', data: { even: false, loading: false }, computed: { numbers: function() {	this.rerender(); return this.even ? _.range(0,100000,2) : _.range(0,100000); } }, methods: { rerender: function() { var self = this; /* self.loading = true */; //this.$nextTick(() => {	self.loading = true; console.log('re-render start') this.$nextTick(() => { self.loading = false; console.log('re-render end') }) //}) }, } })
<script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/lodash"></script> <div id="app"> <input type="checkbox" value="1" v-model="even" /> even? <div v-if="loading" id="loading"> Loading... </div> <div v-else id="loaded"> <div> <div v-for="number in numbers" :key="number">{{number}}</div> </div> </div> </div>

The sample shows a list of numbers 1-100000, and when you click the "even ?" checkbox filter, it causes the list to update with only even numbers. 100000 was used to show the slight delay in the rendering of the new list. If you inspect the DOM, you can see that after checking/unchecking the checkbox, the <div id="loading"> pops into the DOM for a short bit, then changes over to the new list inside <div id="loaded">. Although the loading <div> is shown in the DOM, it is never updated in the UI.

I have code I hoped would show "loading" when the computed property is called, and when the list is done rendering, hide it and show the list. Why is this not working? Why does the DOM change, but the UI is never updated? Is the UI not updating because all the dynamic elements are in the same component and it is not flushed until the list is done rendering? Is there a better way to achieve this?

Thanks!

7
  • I suggest using v-show to hide the display with the data being filtered, and show the loading image Commented Jun 1, 2018 at 22:20
  • From the docs: The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element. Commented Jun 1, 2018 at 22:21
  • Thus, your data can still render in the background while you wait with the loading image active Commented Jun 1, 2018 at 22:21
  • Post your code here, links can rot and users shouldn't have to go to a 3rd party website to view the code. Use JSFiddle and like sites for demonstration only, even then SO has its own runnable code tool called Stack Snippets which you can get to by using the <> button in the editor Commented Jun 1, 2018 at 22:22
  • 1
    Also note long computations like generating 100000 is going to tie up the rendering thread, so the DOM can't update til the processing of the number generation is done. Thus by the time it can update everything is already done and your flag for hiding the loading message is set. Commented Jun 1, 2018 at 22:27

1 Answer 1

2

You are doing a long synchronous process. These types of processes tie up the UI thread, ie you cannot do anything within the tab nor can the DOM be updated during that time.

To do long processes like you are doing you should put it into a Web Worker. Using the Messaging api you would then send a message to the worker telling it to make the list, and it would send a message back when it is done. This would make your process asynchronous and not tie up the UI.

Side note: you might want to think about using pagination for displaying such a large list. As the hiding/showing of that many elements will also cause the browser to lock up.

html

<input type="checkbox" value="1" v-model="even" @change="onEvenChanged" /> even? 

worker.js

importScripts("lodash.js"); self.addEventListener('message', function(e) { var data = e.data; if(data.command == "start"){ let list = data.even ? _.range(0,100000,2) : _.range(0,100000); self.postMessage({"list":list}); } }, false); 

app.js

var app,msgBus; var worker = new Worker('worker.js'); //listen for messages coming back from the worker worker.addEventListener('message', function(e) { var data = e.data; if(data.list){ //emit a message to your vue app that the list //was made msgBus.$emit('listGenerated',data.list); } }, false); //separate empty vue for events msgBus = new Vue(); app = new Vue({ el: '#app', mounted:function(){ //Listen for a listGenerated event //set numbers to the passed list msgBus.$on("listGenerated",(list)=>{ this.numbers = list; this.loading = false; }); }, methods:{ onEvenChanged:function(){ this.loading = true; //send the start command to the worker //passing also the even property this.$nextTick(()=>{ worker.postMessage({command:"start",even:this.even}); }); } } }); 

Demo on plunker

Sign up to request clarification or add additional context in comments.

3 Comments

I specifically wanted to avoid using pagination. Your example works great! I'll learn more about Web Workers, as I haven't used them before. Thank you!
Since the Web Worker only works on regenerating the array of numbers, how does the "Loading..." message disappear when the entire list's HTML is rendered and not before? Is this because of the $on("listGenerated") updating both 'numbers' and 'loading' variables, that don't get reflected in the DOM until 'loading' has been completely re-rendered by Vue?
@haferje Correct, the list variable and loading flag get updated at the same time. Vue will then detect that both were changed and update the respective DOM for both in the same cycle. So loading hides at the same time that the list gets updated and shown.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.