Forzar la actualización de los accesorios en el componente secundario después de la actualización de Vuex

Aug 21 2020

Tengo un componente secundario ( <BaseProjectTable>) que se reutiliza en todo mi sitio y que contiene un <v-data-table>componente de Vuetify . Los encabezados y los elementos de contenido de la tabla de datos se pasan al componente como accesorios, y los datos del elemento se asignan al padre como mapGetterdesde una tienda Vuex. El componente secundario contiene funcionalidad de edición para cada fila, y estoy usando mapActionpara actualizar la API y los datos de Vuex desde allí, con la idea de que, dado que mi mapGetter es reactivo, debería actualizar los datos y, por lo tanto, la visualización de la tabla de datos. Sin embargo, esto no está funcionando; Puedo ver a través de las herramientas de desarrollo que el estado está actualizado muy bien, pero la pantalla del componente secundario no.

Aquí está la parte relevante del <BaseProjectTable>componente secundario :

<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])
          })
        })
      },
    }

y aquí está el código relevante del componente principal:

<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'
    }),
  }
}

El save()método actualiza los datos en mi tienda Vuex a los que hace referencia el activeProjectscaptador de mapas (he verificado en las herramientas de desarrollo de Vue que esto es cierto). También muestra el itemsvalor en el propio componente actualizado en la Componentspestaña de las herramientas de desarrollo. Dado que el uso mapGettershace que los datos sean reactivos, esperaba que también actualizara los datos en mi componente secundario, pero no es así.

Basado en lo que vi aquí , probé la item-keypropiedad de < v-data-table>así:

<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"
>

(cada registro que use este componente tendrá la sowclave única ), pero eso no funcionó.

Lo único que podría pensar es cómo estoy editando los datos en mi mutación:

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;
  }
}

en comparación con reemplazar todo el state[projectType]valor.

¿Qué debo hacer para que la tabla de datos muestre mi valor actualizado?

Respuestas

Tony Aug 21 2020 at 12:40

De la documentación de Vue ,

Vue no puede detectar los siguientes cambios en una matriz

  • Cuando establece directamente un elemento con el índice, por ejemplo, vm.items [indexOfItem] = newValue
  • Cuando modifica la longitud de la matriz, por ejemplo, vm.items.length = newLength

Reemplaza lo que tienes con

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

Después de esto, se detectarán los cambios en la matriz y el captador funcionará como se esperaba.