I have Vue.js app that fetch some complex data from API with Axios and then visualize this data.
My code looks similar to this:
{
data: () => ({
data: null, // or Object, it doesn't matter now
loading: false,
errored: false,
loadingMessage: ''
}),
methods: {
loadData: function() {
this.loading = true;
this.loadingMessage = 'Fetching Data';
axios.get('api/url/').then((response) => {
this.data= response.data; // or this.$set(this, 'data', response.data), it doesn't matter now
this.loadingMessage = 'Process Data';
this.processData();
})
.catch(function () {
this.errored= true;
})
.then(function () {
this.loading = false;
})
},
processData: function() {
// process data
}
}
}
So then I click on the button in template, this button calls loadData() function.
It works fine, but fetching data takes some time and processing also takes some time and Vue change template and variables only when axios request is finished. So I see only Fetching Data message but not Process Data.
How can I show the user at what stage of processing the data now?
Maybe I should call the processData() function in watch methods, but that seems overkill to me.
Update
I ended up with setTimeout() wrap. See my answer below.
Vue has a function called nextTick, which is an asynchronous function that basically means "perform the following code after the next visual update". I usually use this method if I have visual updates like this to make.
I think the code in your example would look like this:
axios
.get('api/url/')
.then((response) => {
this.data= response.data;
this.loadingMessage = 'Process Data';
return this.$nextTick();
})
.then(() => {
this.processData();
})
.catch (etc ...
I am not completely sure. I usually work in webpack/babel-enabled environments, in which case I would just make the whole function async and write:
async function fn() {
const response = await axios.get('api/url/');
this.data = response.data;
this.loadingMessage = 'Process Data';
await this.$nextTick();
this.processData();
}
You can read about it here (https://v2.vuejs.org/v2/guide/reactivity.html#Async-Update-Queue)
Can you try changing your function as follows:
loadData: function() {
this.loading = true;
this.loadingMessage = 'Fetching Data';
axios.get('api/url/').then((response) => {
this.data= response.data; // or this.$set(this, 'data', response.data), it doesn't matter now
this.$nextTick(() => {
this.loadingMessage = 'Process Data';
this.processData();
})
})
.catch(function () {
this.errored= true;
})
.then(function () {
this.loading = false;
})
},
I tried to use $nextTick(), as #joachim-bøggild advised me. I even tried to use $forceUpdate() to archieve this goal. But for some reason that was not clear to me, the effect I needed was not observed. The console showed that the value has changed. But on the screen and in Vue Devtools old results were shown until the request was completed.
So I decided to supplement the question with an example and started to create demo on JsFiddle. To show the rendering delay that I need, I used setTimeout(() =>{ this.loading = false}, 1000) and there were no problems at all.
I ported this approach to my code and everything worked perfectly. Until I tried to remove this setTimeout(). The problem arose again.
Therefore, in the end, I ended up on this code design:
...
axios.get('api/url/').then((response) => {
this.data= response.data;
this.loadingMessage = 'Process Data';
setTimeout(() => {
this.processData();
this.loading = false;
}, 10); // if this value less than 10 error still occurs
})
.catch(function () {
this.errored= true;
})
...
If someone understands why this behavior is happening, please complete my answer.
Related
So I am sure I am messing something up, but I am not super skilled at API.
So I am trying to make an API call to check if the user exists, if user exists then move about business, if not then do other stuff.
So my first call gets the data, and the user DOES exist, the hook is setting to true, however in my log it fails and the next API is ran. However if I do it a 2nd time, it is true...
What am I doing wrong.
const handleSubmit = async () => {
const data = await axios
.get(`URL`, {
})
.then((resp) => {
if (resp.data.user.name) {
setCheckUser(true);
console.log(resp.data.user.name);
}
return data;
})
.catch((err) => {
// Handle Error Here
console.error(err);
});
console.log(checkUser);
if (!checkUser) {
console.log('No User Found');
//Do Stuff//
}
};
I think the problem here is that setCheckUser(true) is an async operation, so there is no guarantee that the checkUser variable will turn to true right away.
Maybe you can solve this by using a useEffect block like this
//somewhere on the top of your file, below your useState statements
useEffect(()=> {
if (!checkUser) {
console.log('No User Found');
//Do Stuff//
}
}, [checkUser])
const handleSubmit = async () => {
const data = await axios
.get(`URL`, {
})
.then((resp) => {
if (resp.data.user.name) {
setCheckUser(true);
console.log(resp.data.user.name);
}
return data;
})
.catch((err) => {
// Handle Error Here
console.error(err);
});
};
How do i add multiple get posts in Vue.js.
I already have one post that I'm getting fine but I'm not sure how to add multiple post functions.
This is what i have so fare.
<script>
new Vue({
el: '#app',
data () {
return {
searchQuery: null,
info: null,
loading: true,
errored: false
}
},
mounted: function () {
axios.post('https://api.npms.io/v2/search?q=vue')
.then(response => {
this.info = response.data
console.log(this.info)
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
}
})
</script>
If you want to do the calls one after the other then
you can just nest the second axios call inside the first one.
In this way you can keep nesting to multiple levels
axios.post('https://api.npms.io/v2/search?q=vue').then(response => {
this.arrayOne = response.data
axios.post('https://api.npms.io/v2/search?q=vue').then(
response => this.arrayTwo = response.data
);
});
OR
You can try using async/await
Below is an example where I have used the response of first request to make the second request.
async mounted() {
try {
const response1 = await axios.get('/user/12345');
this.arrayOne = response1.data;
const response2 = await axios.get(`/user/12345/${this.arrayOne.name}/permissions`);
this.arrayTwo = response2.data;
} catch(e) {
console.log(e);
}
}
I would suggest taking a look at Promise.all() (link to MDN docs). You could do something like:
Promise.all([
axios.post('https://api.npms.io/v2/search?q=vue'),
axios.post('https://example.com/...')
]).then(responses => {
console.log(responses);
// will output an array with responses[0] equals to the data of the first call and responses[1] equals to the data of the second call
})
// Add your catch and finally clauses here...
The benefits of this approach is that your calls are made in parallel but the then clause will only be reached when they both ended.
I am new to coding.In my app after the submission of a form i will get a message that i have submitted successfully or is it an error.After getting the message i want to revert back the user to my previous page with in 5 seconds.while using $router.push getting 'can not read property of undefine push'If some one knows please...
this the scrip to call
enter code here
methods: {
submitForm() {
formService.hospital({
firstName: this.firstName,
,
date: new Date(this.date),
time: this.time
}) .then(response => {
response.data;
console.log(response);
this.isSuccessMessage = true;
this.isErrorMessage = false;
this.$store.dispatch('addPickupAssistanceMessage');
setTimeout(function(){ this.$router.push('/dashboard'); 5000 });
}).catch(error => {
console.log("Error reported from endpoints :", JSON.stringify(error.response));
this.isErrorMessage = true;
this.$store.dispatch('addErrorMessage')
return (this.errorMessage = JSON.stringify(
error.response.data.errorMessage
))
});
},
The problem is inside the handler function passed to setTimeout, which uses this.$router, which is a property of the instance. The problem can be solved with arrow functions.
setTimeout(() => { this.$router.push('/dashboard'); }, 5000);
I'm trying to set data from an axios response but it seems to me like "this" is only in the scope of the axios function. I have tried different variations of the same code that I've seen on other posts, but none are working.
data: () => ({
storeKey: 'dayspanState',
calendar: Calendar.months(),
readOnly: false,
defaultEvents: [],
ticket_event: [],
}),
created(){
this.get_tickets();
console.log(this.ticket_event);
},
methods:
{
get_tickets(){
axios.get('/api/get_patching_tickets')
.then(function (response) {
this.ticket_event = response.data;
}.bind(this));
},
}
Second trial
created(){
var self = this;
axios.get('/api/get_patching_tickets')
.then(function (response) {
self.ticket_event = response.data;
});
console.log(this.ticket_event);
}
Any help would be appreciated.
Try rewriting your function like:
created(){
axios.get('/api/get_patching_tickets')
.then((response) => {
this.ticket_event = response.data;
}).finally(() => {
console.log(this.ticket_event);
});
/* WARNING: the following console will not work as expected
as the local value is set after the successful call
while this is fired immediately after created is called
*/
console.log(this.ticket_event);
}
The callbacks you passed to .then in axios.get are fine. I see the only problem with your code is that it logs this.ticket_event right after calling this.get_tickets() - an asynchronous operation, so it'll not log the updated value after the api call finish because this.get_tickets() operates asynchronously:
this.get_tickets(); // is an async operation
console.log(this.ticket_event); // will not get the most updated value of this.ticket_event
Try this to see if it works:
data() {
return {
storeKey: 'dayspanState',
calendar: Calendar.months(),
readOnly: false,
defaultEvents: [],
ticket_event: [],
}
},
methods: {
get_tickets() {
return axios.get('/api/get_patching_tickets')
.then(response => {
this.ticket_event = response.data;
});
}
},
created() {
this.get_tickets().finally(() => {
console.log(this.ticket_event);
});
}
I am trying to return some value from this dispatch
this.$store.dispatch('setValue', this.Value)
.then(response => {
console.log(response)
});
In my vuex action I have
.catch(error => {
if (error.response.status === 412) {
return "some message"
}
});
How can I pass the error back to the .vue file where the vuex dispatch is made?
I think the correct way of doing this is to have a status property in your store.
Your status object would consist out of error, success, loading.
So if your action throw exception you can handle it like this:
catch (error) {
commit("error", `Some Message`);
}
Your error mutation would look like this:
error(state, payload) {
state.status.success = false;
state.status.loading = false;
state.status.error = payload || false;
}
Your template would just listen on the store.state.status
<div v-if="store.state.status.error">{{store.state.status.error}}</div>
I might be wrong but in my personal opinion I feel it is wrong to use actions to return stuff. Your using the store so might as well leverage it best you can.
Other extra benefits is, you can indicate to your .vue file if api is loading or when something is successful.
What I ended up doing was pretty simple. I chained the catch to my dispatch:
this.$store.dispatch('setValue', this.Value)
.then(response => {
console.log(response)
})
.catch(error => {
if (error.response.status === 412) {
return "some message"
}
});
Then I returned the Axios call from the action:
return axios({
method: 'post',
url: `/mypath,
data: mydata,
json: true,
})
This means I could deal with the returned data/errors locally where I wanted to trigger an action.
Store:
.catch(error => {
if (error.response.status === 412) {
throw error
}
});
Vue element with async method:
try{
let response = await this.$store.dispatch('setValue', this.Value)
} catch(error) {
console.log(error)
});