Reputation: 8072
I'm sending 2 api requests before render the page:
const Profile = {
template: '#profile',
attributes: null,
photos: [],
data: function () {
return {attributes: Profile.attributes, photos: Profile.photos};
},
beforeRouteEnter: function (to, from, next) {
function getProfile() {
return axios.get('user/get-profile?access-token=1', {responseType: 'json'});
}
function getPhotos() {
return axios.get('photos?access-token=1', {responseType: 'json'});
}
axios.all([getProfile(), getPhotos()])
.then(axios.spread(function (profile, photos ) {
console.log(profile, photos );
next(vm => {
vm.setProfile(profile);
vm.setPhotos(photos);
})
}));
},
methods: {
setProfile: function (response) {
Profile.attributes = response.data;
console.log(Profile.attributes);
},
setPhotos: function (response) {
Profile.photos = response.data;
console.log(response);
},
}
};
The problem is rendering occures before setProfile
and setPhotos
methods. How to correct render my component?
Upvotes: 13
Views: 46624
Reputation: 3536
As mentioned in the comments, the first async/await solution is a little bit misleading, because all lifecycle methods are synchronous. This works, because the code is transpiled to an synchronous function with an IIFE inside.
Below I have added a few more recent snippets.
Try it with async/await. I've removed beforeRouteEnter
, axios.spread
and added create
.
const Profile = {
template: '#profile',
attributes: null,
photos: [],
data() {
return {
attributes: null,
photos: null,
};
},
async created() {
const getProfile = await axios.get('user/get-profile?access-token=1');
const getPhotos = await axios.get('photos?access-token=1');
this.setProfile(profile);
this.setPhotos(photos);
},
methods: {
setProfile(response) {
this.attributes = response.data;
console.log(this.attributes);
},
setPhotos(response) {
this.photos = response.data;
console.log(response);
},
},
};
Shorter
const Profile = {
template: '#profile',
attributes: null,
photos: [],
data() {
return {
attributes: null,
photos: null,
};
},
async created() {
this.attributes = await axios.get('user/get-profile?access-token=1');
this.photo = await axios.get('photos?access-token=1');
},
};
You can use an async function inside your lifecycle method.
const Profile = {
template: '#profile',
attributes: null,
photos: [],
data() {
return {
attributes: null,
photos: null,
};
},
created() {
const fetchData = async () => {
const { data: attributes } = await axios.get(
'user/get-profile?access-token=1'
);
const { data: photos } = await axios.get('photos?access-token=1');
this.attributes = attributes;
this.photos = photos;
};
fetchData();
},
};
setup()
In Vue 3 you can use async setup()
. If you use this, you must wrap your component with Suspense
. Caution! This API is currently experimental https://vuejs.org/guide/built-ins/suspense.html#suspense=.
<Suspense>
<template #default>
<YourComponent />
</template>
<template #fallback>
<div>Loading ...</div>
</template>
</Suspense>
export default {
name: 'YourComponent',
async setup() {
const { data: attributes } = await axios.get('user/get-profile?access-token=1');
const { data: photos } = await axios.get('photos?access-token=1');
return {
attributes,
photos
}
}
}
Upvotes: 18
Reputation: 1988
Maybe This can help someone:
Try Promise
:
let fetchData = new Promise((resolve, reject) => {
axios.get(YOUR_API)
.then(function (response) {
resolve();
})
.catch(function () {
reject('Fail To Load');
});
});
fetchData.then(
function(success) {
YOUR_SUCCESS_LOGIC
},
function(error) {
console.log(error);
}
);
Upvotes: 0
Reputation: 3578
You should just be able to return the Promise that is returned from calling axios.all like:
return axios.all([getProfile(), getPhotos()])
// .then() => ...
Or you could add a property to the data object and use this to show a loader until all Promises have resolved
const Profile = {
template: '#profile',
attributes: null,
photos: [],
data: function () {
return {attributes: Profile.attributes, photos: Profile.photos, isLoading: true};
},
beforeRouteEnter: function (to, from, next) {
function getProfile() {
return axios.get('user/get-profile?access-token=1', {responseType: 'json'});
}
function getPhotos() {
return axios.get('photos?access-token=1', {responseType: 'json'});
}
axios.all([getProfile(), getPhotos()])
.then(axios.spread(function (profile, memes) {
console.log(profile, memes);
this.isLoading = false
next(vm => {
vm.setProfile(profile);
vm.setPhotos(photos);
})
}));
},
methods: {
setProfile: function (response) {
Profile.attributes = response.data;
console.log(Profile.attributes);
},
setPhotos: function (response) {
Profile.photos = response.data;
console.log(response);
},
}
};
Template code omitted, but you can just switch the content you display based on the isLoading. If you go down this route then probably best to create an abstraction for the loader.
I would also suggest you might want to look at vuex rather than coupling all of your data to any specific component state.
Upvotes: 2