2
votes

I cannot figure out why the details computed property in the following component is not updating when the fetch() method is called:

<template>
  <div>
  {{ haveData }} //remains undefined
  </div>
</template>

<script>
export default {
  props: {
    group: {
      type: Object,
      required: true
    },
  },
  computed: {
    currentGroup() {
      return this.$store.getters['user/navbar_menu_app_current_group'](
        this.group.id
      )

      /*-- which is the following function 
        navbar_menu_app_current_group: state => item => {
        return state.menu.find(m => {
          return m.id == item
        })
        }
      */

      /*-- this function returns an object like so 
        {
          id: 1,
          label: 'kity cats',
        }
        ***details --> IS NOT DEFINED. If I add it to the current group as null, my problem goes away. However, this is a previous API call that does not set the `details` parameter.
      */
    },
    details() {
      let c = this.currentGroup.details
      console.log(c) // returns undefined, which makes sense, but it should be updated after this.fetch() is called
      return c
    },
    haveData() {
      return this.details != null
    }
  },
  methods: {
    async fetch() {
      await this.$store.dispatch(
        'user/navbar_menu_app_details_get',
        this.group.id
      )
      //This is setting the "details" part of the state on menu which is referred to in the computed properties above
      //Previous to this there is no state "this.group.details"
      //If I add a console log to the mutation the action calls, I get what is expected.
    }
  },
  created() {
    if (!this.haveData) {
      this.fetch()
    }
  }
}
</script>

If I change the array items to include details, it works:

{
  id: 1,
  label: 'kity cats',
  details: null  // <-- added
}

The unfortunate part is that the array is created from a large API call, and adding the details seems unnecessary, as it may never be needed.

How can I get the computed properties to work without adding the details:null to the default state?

Attempt 1:

// Vuex mutation
navbar_menu_app_details_set(state, vals) {
  let app = state.menu.find(item => {
    return item.id == vals[0] //-> The group id passing in the dispatch function 
  })

  //option 1 = doesn't work
  app = { app, details: vals[1] } //-> vals[1] = the details fetched from the action (dispatch)

  //option 2 = doesnt work
  app.details = vals[1]

  //option 3 = working but want to avoid using Vue.set()
  import Vue from 'vue' //Done outside the actual function
  Vue.set( app, 'details', vals[1])
},

Attempt 2:

// Vuex action
navbar_menu_app_details_get(context, id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      context.commit('navbar_menu_app_details_set', [
        context.getters.navbar_menu_app_current(id), //-> the same as find function in the mutation above
        apps[id]
      ])
      resolve()
    }, 1000)
  })
}

// --> mutation doesn't work
navbar_menu_app_details_set(state, vals) {
  vals[0].details = vals[1]
},
2
you may want to read vuejs.org/v2/guide/reactivity.html if details doesn't exist, it can't be a reactive property so not sure how you imagine it will act reactively.Keith Nicholas
Per https://vuex.vuejs.org/guide/mutations.html i need to set the new property via Vue.set( originalData, label, newData) which means i need to import vue within the store - which is modular and using Nuxt is there a way to reference the global vue so i do not need to import Vue everywhere?Jujubes
per the link you provided 'Since a Vuex store's state is made reactive by Vue, when we mutate the state, Vue components observing the state will update automatically. This also means Vuex mutations are subject to the same reactivity caveats when working with plain Vue'Keith Nicholas
I pressed enter to quickly - can i get around importing vue into each store module?Jujubes
Seems unnecessary, extra data to import and kind of ugly. But if thats what i need to do thats what i will do. im just curious if there is a cleaner way to do it. Within the mutation i do have access to the this._vm but thats not the same as Vue.set()Jujubes

2 Answers

1
votes

All Objects in Vue are reactive and are designed in a way such that only when the object is re-assigned, the change will be captured and change detection will happen.

Such that, in your case, following should do fine.

app = { ...app, details: vals[1] }
1
votes

The Vue instance is available from a Vuex mutation via this._vm, and you could use vm.$set() (equivalent to Vue.set()) to add details to the menu item:

navbar_menu_app_details_set(state, vals) {
  let app = state.menu.find(item => {
    return item.id == vals[0]
  })

  this._vm.$set(app, 'details', vals[1])
},