Vuex更新数组作为对象的值

时间:2019-02-22 06:51:20

标签: javascript arrays vue.js vuejs2 vuex

我遇到了在略微嵌套的数据结构中更新Vuex存储中的数据的问题。

我的整个项目比下面的要复杂一些,但是我现在遇到的问题已得到简化和设置,因此对于每个项目页面,都会有一个登录页面,包含表格可见性和单个表格组件,其中包含一个Vuetify v-data-table元素,该元素仅将标头和项目作为道具和显示它们。可以通过以下形式将其可视化:

- Project Page
  |- Landing page
     |- Table Visibility
     |- Table Component
        |- v-data-table

一种考虑方式如下: 对于每种动物(项目页面),都有三种不同的品种/类型,每种都有不同的特征。因此,上述结构将变为:

- Dog
  |- Landing page
     |- Table Visibility
     |- German Shepherd
        |- v-data-table
     |- Bull Terrier
        |- v-data-table
     |- Labrador Retriever
        |- v-data-table
- Cat
  |- Landing page
     |- Table Visibility
     |- Russian Blue
        |- v-data-table
     |- British Shorthair
        |- v-data-table
     |- Persian
        |- v-data-table
- Bird
  |- Landing page
     |- Table Visibility
     |- Cockatiel
        |- v-data-table
     |- Parrot
        |- v-data-table
     |- Macaw
        |- v-data-table

当用户导航到任何项目页面时,他或她将能够通过表可见性组件选择哪些表是可查看的。该组件看起来像这样:

TableVisibility.vue ###-> tableTitle现在作为道具从父组件中拉入

<template>
  <div>
    <v-card>
      <v-card-title>
        <p class="title ma-0">Table Visibility</p>
      </v-card-title>
      <v-divider class="mx-5"></v-divider>
      <v-card-text>
        <v-layout row wrap fill-height>
          <v-checkbox
            v-for="(title, idx) in tableTitles"
            v-model="tableVisibility"
            :label="title"
            :value="title"
            :key="idx"
            class="mx-1"
            multiple
          ></v-checkbox>
        </v-layout>
      </v-card-text>
      <v-card-actions>
        <v-switch
          v-model="showAll"
          :label="showAll ? 'Hide All' : 'Show All'"
        ></v-switch>
        <v-spacer></v-spacer>
      </v-card-actions>
    </v-card>
  </div>
</template>

<script>
  import { mapState } from 'vuex'
  export default {
    name: "TableChoices",
    props: ['tableTitles'],
    data() {
      return {
        showAll: false,
        displayTables: [],
      }
    },
    methods: {
    },
    computed: {
      ...mapState({
        pageName: state => state.pageName,
      }),
    },
    watch: {
      showAll(bool) {
        bool ?
          this.displayTables = this.tableTitles :
          this.displayTables = []
      },
      displayTables: {
        handler() {
          let tableObj = {};
          this.tableTitles.forEach(title => { tableObj[title] = this.displayTables.indexOf(title) > -1 })
          this.$store.commit('setTableVisibility', {page: this.pageName, tables: tableObj})
          if (this.displayTables.length === this.tableTitles.length) {
            this.showAll = true
          } else if (this.displayTables.length === 0) {
            this.showAll = false
          }
        }
      },
    }
  }
</script>

<style scoped>

</style>

LandingPage.vue

<template>
  <div>
  </div>
</template>

<script>
  import Dog from '@/components/Dog'
  import Cat from '@/components/Cat'
  import Bird from '@/components/Bird'
  import { mapState, mapGetters } from 'vuex'
  export default {
    name: "LandingPage",
    components: {
      Dog,
      Cat,
      Bird,
    },
    data() {
      return {
        items: {},
        headers: {},
      }
    },
    computed: {
      ...mapState({
        pageName: state => state.pageName,
      }),
      ...mapGetters({
        tableVisibility: 'getTableVisibility'
      })
    },
    watch: {
      tableVisibility: {
        handler() { console.log('tableVisibility in LandingPage.vue', this.tableVisibility)},
        deep: true
      },
    }
  }
</script>

<style scoped>

</style>

放置Dog,Cat或Bird组件没有意义,因为它们只保留Vuetify数据表,因此它们基本上可以由占位符简单地使用,因为它们并不重要。

商店的设置如下:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export const debug = process.env.NODE_ENV !== 'production'

function initialState() {
  return {
    pageItems: {},
    pageHeaders: {},
    pageName: '',
    tableTitles: {
      Dog: ['German Shepherd', 'Bull Terrier', 'Labrador Retriever'],
      Cat: ['Russian Blue', 'British Shorthair', 'Persian'],
      Bird: ['Cockatiel', 'Parrot', 'Macaw']
    },
    tableVisibility: {
      Dog: {
        German Shepherd: false, 
        Bull Terrier: false, 
        Labrador Retriever: false
      },
      Cat: {
        Russian Blue: false,
        British Shorthair: false,
        Persian: false
      },
      Bird: {
        Cockatiel: false, 
        Parrot: false,
        Macaw: false
      }
    }
  }
}

const state = {...initialState()}

const mutations = {
  setTableVisbility(state, payload) {
    const page = payload.page;
    const tables = payload.tables;
    // Vue.set(state.tableVisibility, page, tables)
    state.tableVisibility[page] = Object.assign({}, tables);
  }
}

const getters = {
  getTableVisibility: (state) => ( state.tableVisibility[state.PageName] ),
  getCurrentPageTableTitles: (state) => ( state.tableTitles[state.pageName] ),
}
export default new Vuex.Store({state, mutations, getters, strict: debug})

就像我说的那样,整个项目要比这复杂得多(我在这里显示的商店功能实际上是在模块中,等等),但是我什至在LandingPage.vue中获取tableVisibility的更新值时都遇到了麻烦零件。我正在尝试将 deep 处理程序应用于观察者的几种不同方法(https://vuejs.org/v2/api/#watchhttps://medium.com/@stijlbreuk/six-random-issues-and-their-solutions-in-vuejs-b16d470a6462);我试图通过使用拼接(How do I empty an array in JavaScript?)清除数组,然后将所有元素推回到数组中来拆分存储中的数组功能。我尝试使用 filter 数组方法尝试创建一个新数组;我尝试将tableVisibility存储为一个对象(表名作为键,值存储为true或false),以便可以使用Object.assign(https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats);我已经找到了有关规范化数据(https://forum.vuejs.org/t/vuex-best-practices-for-complex-objects/10143/2)的页面,但看上去它并不复杂到已经很简单的程度。

我精疲力尽,想弄清楚发生了什么,我非常感谢您的帮助。

编辑

我已经更新了项目,使其包含吸气剂,反应性在tableVisibility值之外的所有其他组件中都起作用。我尝试将tableVisibility元素保留为数组,然后尝试将其转换为对象,如本次编辑所示,然后尝试使用Vue.set更新值,但该方法不起作用,然后尝试使用Object.assign如当前版本所示。如果我离开页面然后返回更新值,则尽管我尝试使用Vue.set和Object.assign,但它们似乎正在失去反应性。

1 个答案:

答案 0 :(得分:1)

我对您的建议是开始考虑标准化数据。一些大问题是:

  • 页面和表名在您的状态下完全是硬编码的字符串。如果您需要扩展页面/表的数量,则会引起麻烦。 tableVisibility也是如此。
  • 当您没有任何getter方法时,您的状态就会被深深嵌套,这迫使您将状态结构公开给Vue组件,这使得更新Vuex存储非常困难。

首先,编写吸气剂方法和变异方法;使它们足够通用,以便您可以在Vue组件中的任何地方重用,并强制所有Vue组件使用它们,而不是直接访问状态

示例:

const getters = {
    getCurrentActivePage: (state) => {}, // return pageName
    getProjects: (state) => {},
    getProjectById: (state) => (projectId) => {},
    getTablesOfProjects: (state) => (projectId) => {},
    getTable: (state) => (tableID) => {},
    isTableVisible: (state) => (tableID) => {}
}

const mutations = {
    setTableVisibility: (state, { tableID, newValue }) => {}
}

在将UI逻辑与状态结构分离之后,可以开始标准化状态数据。尝试将您的状态视为一个小型数据库,并进行逻辑设计。一种方法是这样的:

const state = {
    pageName: '',
    projects: {
        '123': { id: '123', name: 'Dog' },
        '234': { id: '234', name: 'Cat' }
        // ...
    },
    tables: {
        '123z': { id: '123z', projectId: '123', name: 'German Shepherd', visible: false }
    }
}

其中的id可以使用诸如nanoid之类的npm软件包自动生成。

即使没有规范化,我仍然建议您执行步骤1(去耦)。如果商店很复杂,则无法确定修改商店的状态是否会导致Vue组件崩溃。好的gettersmutations将至少捕获无效数据并返回组件的默认值。