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.
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)
}