Search code examples
vuejs2vuexquasar-frameworkquasar

Changes to vuex data not reflected in Quasar QTable


I'm using Quasar with Vuex to load a data set into QTable. It's working great, but I'm trying to use QPopupEdit to change the status of a project. The data is pulled from a Laravel API, the original data set is a Laravel Resource, so I have Items, each having a Status object (item.status_id and status: id, status: name etc)

I have exposed the values of the status for the first record outside of the q-table and when I push the update to the API I return the changed Item resource, inserting it into the Vuex data in the vuex mutation, it does not update in the table.

<div v-if="items && items[0] && items[0].order">
  {{items[0].status.id}} | {{items[0].status.name}}
</div>

<q-table
  title="Projects"
  :data="items"
  :columns="columns"
  color="primary"
  row-key="id"
  :loading="loading"
  no-data-label="no projects with search prameters"
  :visible-columns="visibleColumns">
  <template v-slot:top="props">
    <q-btn
      flat round dense
      :icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
      @click="props.toggleFullscreen"
      class="q-ml-md"
    />
    <q-space />
    <q-select
      v-model="visibleColumns"
      multiple
      borderless
      dense
      options-dense
      emit-value
      map-options
      :options="columns"
      option-value="name"
      style="min-width: 150px"
    />
  </template>
  <template v-slot:body="props">
    <q-tr :props="props">
      <q-td key="user" :props="props">
        {{ props.row.order.user.firstname }} {{ props.row.order.user.lastname }}
      </q-td>
      <q-td key="name" :props="props">
        {{ props.row.service.name }}
      </q-td>
      <q-td key="status" :props="props">
        <q-badge v-if="props.row.status" :color="props.row.status.color" outline>
          {{ props.row.status.name }}
        </q-badge>
        <q-popup-edit v-model="props.row.status.id" :key="item">
          <q-select fill-input color="primary" v-model="props.row.status.id" :key="props.row" :options="statuses" label="Status" emit-value map-options option-label="name" @input="(val) => saveStatus(val, props.row, index)" />
        </q-popup-edit>
        {{props.row.status.id}} | {{props.row.status_id}}
      </q-td>
      <q-td key="updated_at" :props="props">
        {{ formatDate(props.row.updated_at, 'MMM D, YYYY HH:mm A')}}
      </q-td>
      <q-td key="notes" :props="props">
        <div class="table-description cursor-pointer">
          <q-icon v-if="!props.row.notes" name="fal fa-comment-alt" class="float-right" />
          {{ props.row.notes }}
          <q-popup-edit
            buttons
            v-model="props.row.notes"
          >
            <q-input
              type="textarea"
              v-model="props.row.notes"
              autofocus
              counter
              @keyup.enter.stop
            />
          </q-popup-edit>
        </div>
      </q-td>
      <q-td key="id" :props="props">
        <q-btn icon="fal fa-arrow-right" :to="'/item/' + props.row.id" class="cursor-pointer" />
      </q-td>
    </q-tr>
  </template>
</q-table>

An excerpt from the script

import { mapState } from 'vuex'
import { date } from 'quasar'
export default {
data () {
  return {
    inFullscreen: false,
    visibleColumns: [ 'user', 'name', 'status', 'updated_at', 'notes', 'id' ],
    columns: [
      { name: 'user', align: 'left', label: 'Client', field: row => row.order.user.firstname, format: (val, row) => `${val}`, sortable: false },
      { name: 'name', required: true, label: 'Name', align: 'left', field: row => row.service.name, format: val => `${val}`, sortable: true },
      { name: 'status', align: 'left', label: 'Status', field: row => row.status.name, format: val => `${val}`, sortable: true },
      { name: 'updated_at', align: 'left', label: 'Last Updated', field: row => row.status.updated_at, format: val => `${val}`, sortable: true },
      { name: 'notes', align: 'left', label: 'Notes', field: 'notes', sortable: false },
      { name: 'id', align: 'right', label: 'Actions', field: 'id', sortable: false }
    ]
  }
},
mounted () {
  this.fetch()
  if (!this.statuses || this.statuses.length < 1) {
    this.$store.dispatch('items/fetchStatuses')
  }
},
computed: {
  ...mapState({
    loading: state => state.auth.loading,
    authuser: state => state.auth.user,
    pagination: state => state.items.pagination,
    items: state => state.items.items,
    statuses: state => state.items.statuses
  })
},
methods: {
  saveStatus (value, item, index) {
    console.log(value + ' | ' + index)
    console.log(item)
    let payload = { 'status_id': value, 'item_id': item.id, 'index': index }
    this.$store.dispatch('items/saveStatus', payload)
  },
  formatDate (dt, mask) {
    return date.formatDate(date.extractDate(dt, 'YYYY-MM-DDTHH:mm:ss.SSSSSSZ'), mask)
  },
  fetch () {
    this.$store.dispatch('auth/statusLoading', true)
    this.$store.dispatch('items/fetchItems', this.id).then((res) => {
      this.$store.dispatch('auth/statusLoading', false)
    }).catch(error => {
      if (error) {
        console.log(error)
      }
    })
  },
}
}

And my action and mutation from the vuex items module

export function saveStatus ({ state, commit, getters }, payload) {
  return new Promise((resolve, reject) => {
    axiosInstance.post('/api/status/update/Item/' + payload.item_id, payload)
      .then(response => {
        // alert(JSON.stringify(response, null, 4))
        let pl = { 'item': response.data.data }
        commit('setIndexItem', pl)
        resolve(null)
        // console.log(response)
        // commit('setItems', response.data.data)
      })
      .catch(err => {
        reject(err)
      })
  })
}

export function setIndexItem (state, payload) {
  var index = state.items.findIndex(i => i.id === payload.item.id)
  state.items[index] = payload.item
}

I know the Laravel API part is working - the value is updating in the database and the item, with updated status is being returned to the vuex store and that's being updated in the vuex repository as reflected in the exposed, first element for debugging, but in the QTable, it does not update.


Solution

  • Maybe the Array Change Detection leads the view not updated. You can use the Vue.set to update the view

    import Vue from 'vue'
    
    // ...
    
    export function setIndexItem (state, payload) {
      var index = state.items.findIndex(i => i.id === payload.item.id)
      Vue.set(state.items, index, payload.item)
    }