1. Introduction
Since the birth of Ajax technology in 2005, the web development industry has been changing rapidly, moving a lot of works from server-side to the client-side. Now, the web application is behaving much like desktop application due to the impressive reactivity. Such web application is known as Single Page Application in which Javascript plays a major role.
Javascript allow the web page to reload only a part of the webside instead of full page load. However, because all the loading work happens silently in the background, it might confuse the users since they don’t know what’s actually going on as they interact with our website. That’s the reason why handling loading indicator becomes a crucial task that every web developer has to tackle at some point in the development process.
2. Technique
In this post, I’m gonna share with you one way to handle loading indicator that I found very effective, but most importantly, it’s very simple and easy to implement.
The core idea of this technique is using a requestsCounter
variable to store the number of pending requests. Whenever a request is sent, we increase the requestsCounter
by one, and decrease it when the request is done. The implementation might look like this in Vuex – Vuejs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">INCREMENT</span> <span class="token operator">=</span> <span class="token string">'INCREMENT'</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">DECREMENT</span> <span class="token operator">=</span> <span class="token string">'DECREMENT'</span> <span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">RESET</span> <span class="token operator">=</span> <span class="token string">'RESET'</span> <span class="token keyword">export</span> <span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token punctuation">{</span> requestsCounter<span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">const</span> mutations <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span><span class="token constant">INCREMENT</span><span class="token punctuation">]</span> <span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span> state<span class="token punctuation">.</span>requestsCounter <span class="token operator">+=</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token constant">DECREMENT</span><span class="token punctuation">]</span> <span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>state<span class="token punctuation">.</span>requestsCounter <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> state<span class="token punctuation">.</span>requestsCounter <span class="token operator">-=</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token constant">RESET</span><span class="token punctuation">]</span> <span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span> state<span class="token punctuation">.</span>requestsCounter <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">const</span> actions <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function">startLoading</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> commit <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">commit</span><span class="token punctuation">(</span><span class="token constant">INCREMENT</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">finishLoading</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> commit <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">commit</span><span class="token punctuation">(</span><span class="token constant">DECREMENT</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">forceFinishLoading</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> commit <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">commit</span><span class="token punctuation">(</span><span class="token constant">RESET</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">const</span> getters <span class="token operator">=</span> <span class="token punctuation">{</span> isLoading<span class="token punctuation">:</span> state <span class="token operator">=></span> <span class="token operator">!</span><span class="token operator">!</span>state<span class="token punctuation">.</span>requestsCounter <span class="token punctuation">}</span> |
To be able to interact with the store, we need to provide some interface to it. To achieve that, we’re gonna create a mixin and mix it in every components using Vue.mixin()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">import</span> <span class="token punctuation">{</span> mapActions<span class="token punctuation">,</span> mapGetters <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> computed<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span><span class="token function">mapGetters</span><span class="token punctuation">(</span><span class="token string">'loading'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> $isLoading<span class="token punctuation">:</span> <span class="token string">'isLoading'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> methods<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span><span class="token function">mapActions</span><span class="token punctuation">(</span><span class="token string">'loading'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> $startLoading<span class="token punctuation">:</span> <span class="token string">'startLoading'</span><span class="token punctuation">,</span> $finishLoading<span class="token punctuation">:</span> <span class="token string">'finishLoading'</span><span class="token punctuation">,</span> $forceFinishLoading<span class="token punctuation">:</span> <span class="token string">'forceFinishLoading'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
In components, the usage of this technique might look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token function">created</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">loadData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> methods<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token function">loadData</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> service <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Service</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">$startLoading</span><span class="token punctuation">(</span><span class="token punctuation">)</span> service<span class="token punctuation">.</span><span class="token function">getData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>response <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// do something</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span>errors <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// handle errors</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">finally</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">$finishLoading</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
In the App component, it would be like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>div id<span class="token operator">=</span><span class="token string">"app"</span><span class="token operator">></span> <span class="token operator"><</span>router<span class="token operator">-</span>view <span class="token punctuation">:</span>key<span class="token operator">=</span><span class="token string">"$route.fullPath"</span><span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>loading v<span class="token operator">-</span>show<span class="token operator">=</span><span class="token string">"$isLoading"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">import</span> Loading <span class="token keyword">from</span> '@<span class="token operator">/</span>components<span class="token operator">/</span>Loading<span class="token punctuation">.</span>vue <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> components<span class="token punctuation">:</span> <span class="token punctuation">{</span> Loading <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> |
3. Conclusion
The beauty of this technique is the simplicity and effectiveness, especially when we have to deal with multiple concurrent requests. Thank you for reading, let’s me know if you have any better ideas about handling loading indicator.