1
votes

I have a child component (<BaseProjectTable>) that is re-used throughout my site that contains a Vuetify <v-data-table> component. The headers and content items for the data table are passed into the the component as props, and the item data is mapped into the parent as a mapGetter from a Vuex store. The child component contains editing functionality for each row, and I'm using a mapAction to update the API and Vuex data from there, with the idea being that since my mapGetter is reactive, it should update the data and hence the data table display. However, this is not working; I can see via dev tools that state is updated just fine, but the child component display is not.

Here is the relevant portion of the child <BaseProjectTable> component:

<template>
  <div>
    <v-data-table
      show-expand
      :headers="headers"
      :items="filteredItems"
      :search="search"
      tem-key="sow"
      @click:row="rowClick"
      :hide-default-footer="disablePagination"
      dense
      :disable-pagination="disablePagination"
    >
    ...
    </v-data-table>

    ...
    export default {
    name: "BaseProjectTable",
    props: {
      headers: Array,
      items: Array,
      loggedInUser: Object,
      title: String,
      max2chars: v => v.length <= 2 || 'Input too long!',
      editDialog: false,
      showPracticeFilter: {
        default: true,
        type: Boolean
      },
      showSEFilter: {
        default: true,
        type: Boolean
      },
      showStatusFilter: {
        default: true,
        type: Boolean
      },
      projectType: {
        type: String,
        default: 'active'
      },
      disablePagination: {
        type: Boolean,
        default: true
      },
    },
  },
  methods: {
  ...mapActions('projects', {
        saveProject: 'saveProject',
    }), 
    save() {
      // Update API and store with new data.
      this.saveProject({
        projectType: this.projectType,
        projectData: this.editedItem
      })
  },
  computed: {
      statuses()  {
        return this.projectType === 'active' ? this.activeStatuses : this.oppStatuses;
      },
      filteredItems() {
        return this.items.filter(d => {
          return Object.keys(this.filters).every(f => {
            return this.filters[f].length < 1 || this.filters[f].includes(d[f])
          })
        })
      },
    }

and here is the relevant code from the parent component:

<v-card>
  <BaseProjectTable
    :headers="headers"
    :items="activeProjects"
    :loggedInUser="loggedInUser"
    title="Active Projects"
    :disablePagination=false
  ></BaseProjectTable>
</v-card>
...
export default {
  computed: {
    ...mapGetters('projects', {
      activeProjects: 'activeProjects',
      closedProjects: 'closedProjects',
      opportunities: 'opportunities'
    }),
  }
}

The save() method updates the data in my Vuex store that is referenced by the activeProjects map getter (I have verified in the Vue devtools that this is true). It also shows the items value in the component itself updated in the Components tab in the dev tools. Since using mapGetters makes the data reactive, I expected that it would also update the data in my child component, but it doesn't.

Based on what I saw here, I tried the item-key property of the <v-data-table> like so:

<v-data-table
  show-expand
  :headers="headers"
  :items="filteredItems"
  :search="search"
  item-key="sow"
  @click:row="rowClick"
  :hide-default-footer="disablePagination"
  dense
  :disable-pagination="disablePagination"
>

(every record using this component will have the unique sow key), but that didn't work.

The only think I could think if is how I'm editing the data in my mutation:

export const state = () => ({
    active: [],
    closed: [],
    opportunities: [],
})

export const getters = {
  activeProjects: state => state.active,
  closedProjects: state => state.closed,
  opportunities: state => state.opportunities,
}

export const actions = {
  saveProject({ commit }, {projectType, projectData}) {
    commit(types.SAVE_PROJECT, {projectType, projectData});
  }
}

export const mutations = {
  [types.SAVE_PROJECT](state, {projectType, projectData}) {
    // Get project from state list by sow field.
    const index = state[projectType].findIndex(p => p.sow === projectData.sow);
    state[projectType][index] = projectData;
  }
}

as compared to replacing the entire state[projectType] value.

What do I need to do to get the data table to display my updated value?

1
Is active in store the same as activeProjects? Could not correlate the mutation - I may have missed something there. Did you try replacing state[projectType][index] = projectData; with this.$set(state[projectType], index, projectData) - that should ensure reactivity and reflect changes in getter.Prashanth K
Yes, it is (I added the getters to the code above). My problem is that I can see my changes in the getter in the Vue devtools - they're just not being reflected in the display.wonder95

1 Answers

1
votes

From the Vue documentation,

Vue cannot detect the following changes to an array

  • When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
  • When you modify the length of the array, e.g. vm.items.length = newLength

Replace what you have with

import Vue from 'vue'; // this should be at the top level

export const mutations = {
  [types.SAVE_PROJECT](state, {projectType, projectData}) {
    // Get project from state list by sow field.
    const index = state[projectType].findIndex(p => p.sow === projectData.sow);
    Vue.set(state[projectType], index, projectData)
  }
}

After this, the changes to the array will be detected and the getter will work as expected.