1

With my limited knowledge of vue, I am facing the problem where unless I call an async method within the vue js template I can't access the results of the promise even if I am to call the async method in the created life cycle hook. My understanding of created is that it is called earlier so that async operations can be triggered, such as data fetching from an api.

As you'll see in the code below, the function loadAlerts() is called within created. However, unless I call {{loadAlerts()}} in the template I wouldn't be able to use the result of the promise, alerts, in the li that directly follows. The problem of calling {{loadAlerts}} within the template is that [object Promise] is also displayed on the template. If I remove the {{loadAlerts}} from the template than non of my alerts will show up.

Shouldn't the call within created already populate the result of the promise, eg: alerts? As you'll see in the code below I have commented out {{loadAlerts()}} right before looping through the alerts. Without it I don't see any alerts.

What do I need to do so

  1. alerts will still be populated without needing to call loadAlerts within the template (ie, call it within created or mounted instead)

OR

  1. alerts will still be populated but [object Promise] is not displayed

I am guessing, option 1 is probably more elegant and the recommended way?

<template>
 <div v-if="alerts">
  <h4>{{ title }}</h4>
  <!-- {{loadAlerts()}} -->
  <li v-for="alert in alerts" class="alert">
     {{ alert.name }}
  </li>
 </div>
</template>

export default {
  data () {
    return {
      alerts: null
    }
  },

  computed: {
    ...mapGetters('authentication', ['token']),
    ...mapGetters('user', ['profile']),

    title () {
      // Handle the null case
      const alerts = this.alerts || []

      return `My Alerts (${alerts.length})`
    }
  },

  methods: {
    // This needs to be in the methods, not a computed property
    loadAlerts () {
      return getUserAlert({
        user_id: this.profile.user_id,
        token: this.token
      }).then(response => (this.alerts = response.data)).catch(
        error => console.log(error)
      )
    }
  },

  // Initiate loading in a hook, not via the template
  created () {
    this.loadAlerts()
  }
}
</script>
Mark
  • 3,138
  • 5
  • 19
  • 36
  • There should be no reason to call the function in the template. Are you sure the `user_id` and `token` are available at the time the async request is made? Check by alerting them (not logging to console). – Dan Mar 09 '20 at 18:30
  • Good catch Dan, it seems like when I refresh the page token is still there but not the user_id. – Mark Mar 09 '20 at 18:38
  • Ok good, I put this and more explanation into an answer. – Dan Mar 09 '20 at 18:43
  • As a side note, is there a reason why I should be checking with alert and not console.log? – Mark Mar 09 '20 at 18:46
  • 1
    Actually looking again it was probably not necessary in this case. The reason for it is that when checking timing, if checking a previously initialized object or array, the console remembers the reference and will show the data after it comes in even if it wasn't ready at the time of the log. Not necessary here because `user_id` and `token` are probably not objects/arrays, so no reference. – Dan Mar 09 '20 at 18:49
  • You can read more about that [here](https://stackoverflow.com/questions/17546953/cant-access-object-property-even-though-it-shows-up-in-a-console-log) – Dan Mar 09 '20 at 18:51

1 Answers1

1

There should be no reason to call the function in the template. What's probably happening is that user_id and/or token aren't available yet during the created hook. So you would see no results. (Check this by alerting them-- not logging to console.)

When you add the method to the template, if later any re-render happens, the async call will be made again. At some point that probably happens when user_id and token are available, so you see the data.

This is also not a good pattern for that reason: there will be another call to the API every time the template is re-rendered, even after it works once.

One solution is there should be a promise from the user_id/token initial load that you can pass to this component. Then the created hook could register its actions in the then of that promise.

Dan
  • 59,490
  • 13
  • 101
  • 110