Vuex обновляет массив как значение объекта - PullRequest
0 голосов
/ 22 февраля 2019

У меня проблема с обновлением данных в хранилище 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 ### -> tableTitles теперь извлекается как опора из родительского компонента

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

Как я уже сказал, весь проект более сложный, чем этот (функциональность магазина, которую я здесь показываю, фактически находится в модуле и т. д.), но у меня возникают проблемы дажеполучение обновленного значения tableVisibility в компоненте LandingPage.vue.Я пробую несколько различных способов применения обработчика deep к наблюдателю (https://vuejs.org/v2/api/#watch и https://medium.com/@stijlbreuk/six-random-issues-and-their-solutions-in-vuejs-b16d470a6462);. Я попытался разделить функциональность массива в хранилище с помощьюочистка массива с помощью сплайсинга ( Как очистить массив в 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. I 'мы пытались оставить элементы tableVisibility в виде массивов, затем я попытался преобразовать их в объекты, как видно из этого редактирования, и я попытался использовать Vue.set для обновления значений, что не сработало, затем я попытался использовать Object.assign как естьпоказано в этой текущей версии.Если я отошел от страницы, а затем вернулся, значения обновились, просто они, похоже, теряли реактивность, несмотря на мои попытки использовать Vue.set и Object.assign.

1 Ответ

0 голосов
/ 22 февраля 2019

Я предлагаю вам начать думать о нормализации ваших данных.Вот некоторые серьезные проблемы:

  • Имена страниц и таблиц полностью записаны в виде строки в вашем состоянии.Это вызывает проблемы, если вам нужно увеличить количество страниц / таблиц.То же самое относится к tableVisibility.
  • Ваше состояние глубоко вложено, в то время как у вас нет методов получения, которые вынуждают вас предоставлять структуру состояний вашему компоненту 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 }) => {}
}

После отделения логики пользовательского интерфейса от структуры состояний вы можете приступить к нормализации данных о состоянии.Попытайтесь увидеть свое состояние как небольшую базу данных и разработайте ее логически.Один из способов сделать это так:

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 может быть автоматически сгенерирован с использованием пакетов npm, таких как nanoid.

Даже без нормализации я все же рекомендую выполнить шаг 1 (расцепления).Если ваш магазин сложный, вы никогда не можете быть уверены, что изменение состояния магазина приведет к выходу из строя компонентов Vue.Хорошие значения getters и mutations будут по крайней мере перехватывать неверные данные и возвращать значение по умолчанию для вашего компонента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...