Alex
Alex

Reputation: 8072

Fetching data from api before render component

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

Answers (3)

Philip
Philip

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.

Old answer

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');
  },
};

Updated answer

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();
  },
};

Vue 3 and 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

Abolfazl Mohajeri
Abolfazl Mohajeri

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

Purple Hexagon
Purple Hexagon

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

Related Questions