Unable to make loader spinner spinning untill pages loads in VueJS - vue.js

I have added a spinner to my VueJS page where spinner will be shown untill the full page contents load. But the spinner stopps loading instantly it starts loading. It doen't wait for the page fully laods.
Here is my code from App.vue
<loading :active='isLoading' :is-full-page="fullPage" :loader='loader' />
<header-content></header-content>
<router-view></router-view>
<footer-content></footer-content>
data() {
return {
isLoading: false
}
created() {
this.isLoading = true;
/// API CALLS
this.isLoading = false;
}
How can i solve this issue?

You need to change the value of isLoading to false after the data have loaded
<loading :active='isLoading' :is-full-page="fullPage" :loader='loader' />
<header-content></header-content>
<router-view></router-view>
<footer-content></footer-content>
data() {
return {
isLoading: false
},
methods: {
apiCall() {
axios.get(`/data`)
.then((res) => {
this.isLoading = false;
})
.catch((err) => {
this.isLoading = false;
});
}
},
created() {
this.isLoading = true;
this.apiCall()
}

Related

Vue3 Emit Event not triggering method in parent

After making an API call I want to stop a loading spinner from displaying. Before calling the child component I set the this.showLoader property to true. This displays the spinner graphic. However once the API call has been made the graphic does not disapear. The updateLoader method never gets called.
child.vue
export default {
methods: {
fetchData() {
fetch(url, options)
.then((response) => response.json())
.then(this.$emit('hideLoaderEvent', false));
}
}
}
parent.vue
<template>
<MyComponent #hideLoaderEvent="updateLoader" />
</template>
export default {
data() {
return {
showLoader: false,
},
methods: {
updateLoader() {
this.showLoader = false;
}
}
}

Adding delay after vuex state changes

I am trying to add some delay to transitioning out of an element when the value - v-if directive binds to - changes from true to false. I am using Vuex to maintain the state of isLoading so that it is used in other components.
So I have an API call that sets this.$store.state.isLoading to true while waiting for response, and false once response is received. But the problem is that API response is almost instant and the progress bar only flashes for a split second.
<template>
<div>
<b-progress v-if="isLoading" :max="max">
<b-progress-bar :value="count"></b-progress-bar>
</b-progress>
</div>
</template>
<script>
module.exports = {
data() {
return {
count: 0,
max: 100
}
},
computed: {
isLoading () {
return this.$store.state.isLoading;
}
}
}
</script>
Is this the right thinking by adding delay after the state is changed? If so, what is the proper way to do it?
You should add the delay right after your API call, assuming you have a mutation named updateIsLoading to update the isLoading state.
fetch(url)
.then(res => res.json())
.then(res => {
// do something with your data
setTimeout(() => this.$store.commit('updateIsLoading'), 1000)
})
.catch(...)
If you need to set the delay once and for all, one solution is to create an action, and then dispatch the action after you get response from the API call.
actions: {
updateIsLoading({ commit }) {
setTimeout(() => commit('updateIsLoading'), 1000)
}
}
If you really want to do this in your component, you can setup a watcher and update a local isLoading variable after some delay:
export default {
data() {
return {
count: 0,
max: 100,
isLoading: false
}
},
computed: {
loading () {
return this.$store.state.isLoading;
}
},
watch: {
loading(newVal, oldVal) {
setTimeout(() => this.isLoading = newVal, 1000)
}
}
}

How to pass a reactive prop to child in Vuejs

I creating a reusable modal in which I want to pass the prop :loading="loading". But as of now the loading property initialized in data is not reactive. How can I update loading in the parent so it is reactive inside the child component ?
Parent component template:
<child_component :loading="loading"></child_component>
Parent component script:
data() {
return {
loading: false
}
},
onDelete() {
this.loading = true;
setTimeout(function(){ alert("Hello"); }, 3000);
this.loading = false;
},
child :
<v-btn icon
:loading="loading"
<v-icon>{{icon}}</v-icon>
</v-btn>
...
props: { disabled: { Boolean, default: false } }
Move this.loading = false; inside setTimeout and use arrow function as i have used below else you will not get reference to this.
onDelete() {
this.loading = true;
setTimeout(() => { this.loading = false; }, 3000);
}

Loading effect does'nt work while calling a function

I wrote the code to implement a loading effect. However, when I click the button, the loading effect doesn't work. But, if I remove the function, the loading effect works. It seems the function prevent loading effect while it is running. What is the problem?
<template>
<div v-loading="loading">
<center><el-button #click="updateData">Click</el-button></center>
</div>
</template>
data() {
return {
loading: false
}
},
methods: {
updateData() {
this.loading = true
uploadData().then(response => {
const data = response.data
if (data.code === 200) {
this.$message.warning({ message: 'OK', duration: 5000 })
} else {
this.$message.error({ message: data.msg, duration: 5000 })
}
})
this.loading = false
},
....
}
You are calling this.loading = false right after your uploadData() method which is running async. Place the line inside your then() method as follows:
<template>
<div v-loading="loading">
<center><el-button #click="updateData">Click</el-button></center>
</div>
</template>
data() {
return {
loading: false
}
},
methods: {
updateData() {
this.loading = true
uploadData().then(response => {
const data = response.data
if (data.code === 200) {
this.$message.warning({ message: 'OK', duration: 5000 })
} else {
this.$message.error({ message: data.msg, duration: 5000 })
}
this.loading = false
})
},
....
}

Too many requests when controlling spinner show/hide from axois interceptors

I have an SPA written in Vue (Webpack) where I want to control the visibility of a spinner based on whether or not the app is currently handling an HTTP request or a response.
Following some tutorials, I came up with the event bus scheme and did this:
Created eventBus.js:
import Vue from 'vue';
export const eventBus = new Vue();
I'm setting my axios interceptors in the created() hook of App.vue. Here's what the necessary functions look like in that component:
data() {
return {
showLoader: false
};
},
created(){
this.setAxiosInterceptors();
// some code removed //
}
},
mounted() {
eventBus.$on('show-loader', () => {
this.showLoader = true;
});
eventBus.$on('hide-loader', () => {
this.showLoader = false;
});
},
methods: {
setAxiosInterceptors() {
var tokenCookieName = this.$store.getters.getCookieNames.apiToken;
var cookieDefaultValue = this.$store.getters.getCookieDefaultValue;
// token expired middleware
this.axios.interceptors.response.use(response => {
var data = response.data;
if(data.info.api_token) {
this.$cookie.set(tokenCookieName, data.info.api_token);
}
if(data.status == 'error' && data.info.login_failed) {
this.$cookie.set(tokenCookieName, cookieDefaultValue);
window.location = '/'; // not possible to use Vue router here
}
eventBus.$emit('hide-loader');
return response;
},
error => {
eventBus.$emit('hide-loader');
console.log('Response interception failed!');
return Promise.reject(error);
});
// attach API token middleware
this.axios.interceptors.request.use(config => {
var apiToken = this.$cookie.get(tokenCookieName);
if (!apiToken) {
apiToken = cookieDefaultValue;
}
config.headers.Authorization = 'Bearer ' + apiToken;
eventBus.$emit('show-loader');
return config;
},
error => {
eventBus.$emit('hide-loader');
console.log('Request interception failed!');
return Promise.reject(error);
}
);
}
}
Please ignore some of the code that isn't relevant to the problem, but I wanted to show how things are set up. Problem is, as soon as I visit my home page, the app keep making the startup GET requests over and over, until my server returns a 429 error.
Interestingly, in my eventBus.$on handlers, if I just do a console.log, this behavior doesn't appear (of course, the spinner doesn't work as well) but as soon as I change a variable or call a vuex action, this infinite reloading starts.
Any clue?
In the main.js file
Vue.prototype.$axios = axios.create(
{
headers:
{
'Content-Type': 'application/json',
},
baseURL: process.env.API_URL
}
);
Vue.prototype.$axios.interceptors.request.use(
config =>
{
eventBus.$emit('show_spin');
let token = getTokenID();
if(token && token.length) config.headers['Authorization'] = token;
return config;
},
error =>
{
eventBus.$emit('hide_spin');
if (error.status === 401) VueRouter.push('/login');
else throw error;
}
);
Vue.prototype.$axios.interceptors.response.use(
response =>
{
eventBus.$emit('hide_spin');
return response;
},
error =>
{
eventBus.$emit('hide_spin');
return new Promise(function(resolve,reject)
{
if (error.config && error.response && error.response.status === 401 && !error.config.__isRetry)
{
myVue.refreshToken(function()
{
error.config.__isRetry = true;
error.config.headers['Authorization'] = getTokenID();
myVue.$axios(error.config).then(resolve,reject);
},function(flag) // true = invalid session, false = something else
{
if(process.env.NODE_ENV === 'development') console.log('Could not refresh token');
if(getUserID()) myVue.showFailed('Could not refresh the Authorization Token');
reject(flag);
});
}
else throw error;
});
}
);
let myVue = new Vue(
{
el: '#app',
data: function()
{
return {
spin_visible: 0, // dynamically show/hide spinner
};
},
created: function()
{
eventBus.$on('show_spin', this.showSpin);
eventBus.$on('hide_spin', this.hideSpin);
},
methods:
{
showSpin: function()
{
this.spin_visible++;
},
hideSpin: function()
{
if(this.spin_visible>0) this.spin_visible--;
},
....
and then in App.vue
<template>
<router-view/>
<div class="spinner" v-show="$root.spin_visible">
<!-- define your spinner here -->
</div>
</template>