Search code examples
vuejs2vuexstore

VUEJS Can’t use api response data in the template


I need to populate a table using an array of objects got by an api call (axios). This part is working fine. In the store module (activity.js) I declared the array:

currentUserActivities: [],

In the mutations:

SET_CURRENT_USER_ACTIVITIES: (state, currentUserActivities) => {
  state.currentUserActivities = currentUserActivities
},

In the actions:

setCurrentUserActivities({ commit }, userId) {
  return new Promise((resolve, reject) => {
    getUserActivities(userId).then(response => {
      const currentUserActivities = response.results
      commit('SET_CURRENT_USER_ACTIVITIES', currentUserActivities)
      console.log('response current user activities: ', response.results)
      resolve()
    }).catch(error => {
      console.log('Error setting single user activities: ', error)
      reject(error)
    })
  })
},

Then I saved it in the getters module as so:

currentUserActivities: state => state.activity.currentUserActivities,

In the vue page, the relevant part of the script:

data() {
  return {
    currentUser: {},
    userId: {
      type: Number,
      default: function() {
        return {}
      }
    },   
    currentUserActivities: [],
  }
 },
 mounted() {
   const userId = this.$route.params.userId
   this.$store.dispatch('user/setCurrentProfile', userId).then(() => {
     const currentUser = this.$store.getters.currentProfile.user
     this.currentUser = currentUser
     console.log('user mounted user', currentUser)
     this.$store.dispatch('activity/setCurrentUserActivities', userId).then(() => {
       const currentUserActivities = this.$store.getters.currentUserActivities
       console.log('activities on mounted', currentUserActivities)
     })
   }) 
 },

In the template part, as I said, I will have a table data. Let's forget about it for now, I am just trying to get the array displayed raw, as so:

<div>
<p v-if="currentUserActivities.length = 0">
   This user has no activities yet.
</p>
<p>CURRENT ACTIVITIES: {{ currentUserActivities }}</p>
<p>CURRENT USER: {{ currentUser }}</p>
</div>

The current user is displaying fine, in the browser I can see:

CURRENT USER: { "id": 1, "last_login": "20/09/2019 09:42:15", "is_superuser": false, "username": "admin", "first_name": "System", "last_name": "Dev", "email": "[email protected]", "is_staff": true, "is_active": false, "date_joined": "30/08/2019 09:03:40" }

The current user activities array, instead:

CURRENT ACTIVITIES: []

In the console I have both, leaving the user which is fine, the current user activities array is:

activities on mounted: 
0: {...}
1: {…}
2:
activity: (...)
arrival_point: "SRID=4326;POINT (0 0)"
burns_calories: false
co2: "0.00"
co2_production: (...)
cost: (...)
created: (...)
default_cost: (...)
end: (...)

ecc. It's there, we can see it.

Inside the mounted, if we compare the code written for the user and the activities, the only difference is that I didn't set

this.currentUserActivities = currentUserActivities

If I do that, I loose the data in the console too (on the screen it remains empty array). In the console I would have:

activities on mounted: (5) [{…}, {…}, {…}, {…}, {…}, __ob__: Observer]
  1. length: 0
  2. __ob__: Observer {value: Array(0), dep: Dep, vmCount: 0}
  3. __proto__: Array

Also, even if I set

v-if="currentUserActivities.length = 0"

to display a p tag in case the array is really empty, it doesn't get displayed. This too is not right. I don't know if they can be related.

I tried many many subtle different versions of code, but none of them worked.

I know I am missing something (code is never wrong....) ....

Can someone enlighten me, please?

Thanks a lot. x


Solution

  • First up, this:

    this.$store.dispatch('activity/setCurrentUserActivities', userId).then(() => {
      const currentUserActivities = this.$store.getters.currentUserActivities
      console.log('activities on mounted', currentUserActivities)
    })
    

    As you've noted in the question, you aren't assigning currentUserActivities to anything. It should be this:

    this.$store.dispatch('activity/setCurrentUserActivities', userId).then(() => {
      const currentUserActivities = this.$store.getters.currentUserActivities
      this.currentUserActivities = currentUserActivities
      console.log('activities on mounted', currentUserActivities)
    })
    

    I know you mentioned that this didn't work in the question but it is required to get it working. It isn't sufficient, but it is necessary.

    The reason the array appears empty is because of this:

    v-if="currentUserActivities.length = 0"
    

    Note that you are setting the length to 0, not comparing it to 0. It should be:

    v-if="currentUserActivities.length === 0"
    

    You've got some other problems too, though they're not directly related to the empty array.

    Generally you shouldn't have data values for state in the store (unless you're taking copies for editing purposes, which you don't seem to be). Instead they should be exposed as computed properties, e.g.:

    computed: {
      currentUser () {
        return this.$store.getters.currentProfile.user
      }
    }
    

    Vuex includes a helper called mapGetters that can be used to shorten this a little, see https://vuex.vuejs.org/api/#component-binding-helpers, though some people prefer the explicitness of the longer form.

    This is also a little strange:

    return new Promise((resolve, reject) => {
      getUserActivities(userId).then(response => {
    

    Generally creating a new promise is regarded as a code smell as it is very rarely necessary. In this case you should probably just be returning the promise returned by getUserActivities instead. e.g.:

    return getUserActivities(userId).then(response => {
    

    Obviously you'd need to make other adjustments to accommodate the resolve and reject functions no longer being available. Instead of resolve you'd just return the relevant value (though there doesn't seem to be one in your case) and for reject you'd just throw the error instead.

    I also notice that userId in your data is being assigned a type and default. Note that this is prop syntax and isn't valid for data properties. It isn't an error but the userId will just be equal to that whole object, it won't treat it as a configuration object.