Массив обновления 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 ### -> 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.info('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. Я пробую несколько разных методов применения обработчика глубокий к наблюдателю (https://vuejs.org/v2/api/#watch и https://medium.com/@stijlbreuk/six-random-issues-and-their-solutions-in-vuejs-b16d470a6462); Я попытался разделить функциональность массива в хранилище, очистив массив с помощью соединения (Как очистить массив в JavaScript?), а затем вернув все элементы обратно в массив; Я попытался использовать метод массива фильтр, чтобы попытаться создать новый массив; Я попытался сохранить tableVisibility как объект (с именем таблицы в качестве ключа и значением как true или false), чтобы я мог использовать Object.assign (https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Предостережения); Я нашел эту страницу о нормализации данных (https://forum.vuejs.org/t/vuex-best-practices-for-complex-objects/10143/2), но кажется, что это не так уж сложно до такой степени, что это уже довольно просто.

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

Редактировать

Я обновил проект, чтобы включить геттеры, и реактивность работает во всех других компонентах, кроме значения tableVisibility. Я попытался оставить элементы tableVisibility в виде массивов, затем попытался преобразовать их в объекты, как показано в этом редактировании, и попытался использовать Vue.set для обновления значений, что не сработало, затем попытался использовать Object.assign как показано в этой текущей версии. Если я уйду со страницы, а затем вернусь, значения обновятся, просто они, кажется, теряют реактивность, несмотря на мои попытки использовать Vue.set и Object.assign.

Vue.set(state.tableVisibility, page, table): стол должен быть столом?
blaz 22.02.2019 08:08

Это просто опечатка, которую я забыл исправить при переносе кода из моего проекта в этот более простой. Редактирую сейчас

Jeff 22.02.2019 08:18
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
2 882
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Мое предложение для вас, чтобы начать думать о нормализации ваших данных. Некоторые большие проблемы:

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

где идентификатор может быть автоматически сгенерирован с использованием пакетов npm, таких как nanoid.

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

Спасибо за помощь. Я обновил проект по вашему предложению (хотя я еще не нормализовал данные), и он все еще вызывает проблемы. Код в исходном сообщении обновлен, и я был бы признателен, если бы вы могли взглянуть еще раз.

Jeff 23.02.2019 00:11

Ничего, код как написано выше (github.com/JVP3122/vuex-реактивность) работает. В моем основном проекте должно быть что-то еще.

Jeff 23.02.2019 03:04

Другие вопросы по теме