Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$emit inside async method - Vue2

Here's my child's component async method:

async created () {
  this.$parent.$emit('loader', true)
  await this.fetchData()
  this.$parent.$emit('loader', false)
}

fetchData does an axios get call, to fetch data from API. However in a vue-devtools (events tab) i can only see the events, after i change the code and it hot reloads. Also i've set up console.log() in a parent component:

mounted() {
  this.$on('loader', (value) => {
    console.log(value)
  })
}

And i can see only false in a console. My purpose is to emit loader set to true (so i can show the loader), then set it to false, when data is fetched.

My fetchData method:

import http from '@/http'

fetchData() {
  return http.getOffers().then((resp) => {
    this.offersData = resp.data
  })
}

Contents of http.js:

import axios from 'axios'
import config from '@/config'

const HTTP = axios.create({
  baseURL: config.API_URL
})

export default {
  /* calculator */
  getOffers() {
    return HTTP.get('/url')
  }
}

If i directly use return axios.get() in async created(), then it works. Problem is in this imported http instance.

Final solution

One of the problems was using different lifecycles, thanks to Evan for mentioning this.

Another problem was with async / await usage, changes to a fetchData() method:

import http from '@/http'

async fetchData() {
  await http.getOffers().then((resp) => {
    this.offersData = resp.data
  })
}

I had to make this method async and use await on axios request, since await is thenable, it does work. Also i've spotted an issue in https.js:

export default {
  /* calculator */
  getOffers() {
    return HTTP.get('/url')
  }
}

It returns HTTP.get(), not a promise itself, i could have used then here, and it would work, but, for flexibility purposes i didn't do that.

But, still, i don't get why it didn't work:

fetchData() {
  return http.getOffers().then((resp) => {
    this.offersData = resp.data
  })
}

Isn't it already returning a promise, since it's chained with then... So confusing.

Retested again, seems like return is working, lol.

like image 890
Alexander Kim Avatar asked Jul 25 '26 02:07

Alexander Kim


2 Answers

The issue here is that created on the child component is getting called before mounted on the parent component, so you're beginning to listen after you've already started your Axios call.

The created lifecycle event method does not do anything with a returned promise, so your method returns right after you begin the Axios call and the rest of the vue component lifecycle continues.

You should be able to change your parent observation to the created event to make this work:

created() {
  this.$on('loader', (value) => {
    console.log(value)
  })
}

If for some reason you need to do something that can't be accessed in created, such as accessing $el, I'd suggest moving both to the mounted lifecycle hook.

like image 81
Evan Kennedy Avatar answered Jul 27 '26 14:07

Evan Kennedy


I'd simply suggest restructuring your method, as there isn't really a need to make an async method since axios itself is asnychronus.

If you already have the fetchData method defined, and the goal is to toggle the loader state when a call is being made, something like this should do.

fetchData () {
    this.$parent.$emit("loader", true)

    axios.get(url)
        .then(resp => {
            this.data = resp
            this.$parent.$emit("loader", false)
        })
}

Of course these then statements could be combined into one, but it's the same idea.

Edit: (using the parent emit function)

fetchData () {
    this.loader = true

    axios.get(url)
        .then(resp => this.data = resp)
        .then(() => this.loader = false)
}
like image 44
Sebastian Scholl Avatar answered Jul 27 '26 14:07

Sebastian Scholl