VueJs и Vuex, как прикрепить v-модель к динамическим полям

У меня есть ряд флажков, созданных базой данных. Вызов базы данных обычно не завершается до загрузки страницы.

Это часть моего Vue.

folderList - это список папок из базы данных, каждая из которых имеет ключ и doc.label для описания флажка и doc.hash для использования в качестве уникального ключа.

<template>
    <ul>
        <li v-if="foldersList!=null" v-for="folder in folderData">
            <Checkbox :id="folder.key" :name="folder.hash" label-position="right" v-model="searchTypeList[folder.hash]">{{ folder.doc.label }}</Checkbox>
        </li>
    </ul>
</template>
export default {
    name: 'Menu',
    components: {
        Checkbox,
        RouteButton
    },
    props: {
        foldersList: {type: Array, required: true}
    },
    computed: {
        ...mapGetters({
            searchListType: 'getSearchListType'
        }),
        searchTypeList: {
            get() {
                return this.searchTypeList;
            },
            set(newValue) {
                console.log(newValue);
                //Vuex store commit
                this.$store.commit(Am.SET_SEARCH_TYPES, newValue);
            }
        }
    },
    methods: {
            checkAllTypes: _.debounce(function (folderList){
            const initialList = {};
            folderList.forEach((folder) => {
                initialList[folder.hash] = true;
            });
            this.$store.commit(Am.SET_SEARCH_TYPES, initialList);
        }, 100)
    },
    mounted() {
        //hacky way of prefilling data after it loads
        this.$store.watch(
            (state) => {
                return this.foldersList;
            },
            this.checkAllTypes,
            {
                deep: false
            }
        );
    }

Флажок - это настраиваемый компонент со скользящим флажком стиля, его v-модель похожа на эту

<template>
    <div class="checkbox">
        <label :for="id" v-if="labelPosition==='left'">
            <slot></slot>
        </label>
        <label class="switch">
            <input type="checkbox" :name="name" :id="id" :disabled="isDisabled" v-bind:checked="checked" v-on:change="$emit('change',  $event.target.checked)"/>
            <span class="slider round"></span>
        </label>
        <label :for="name" v-if="labelPosition==='right'">
            <slot></slot>
        </label>
    </div>
</template>

<script>
    export default {
        name: 'Checkbox',
        model: {
            prop: 'checked',
            event: 'change'
        },
        props: {
            id: {default: null, type: String},
            name: {required: true, type: String},
            isDisabled: {default: false, type: Boolean},
            checked: Boolean,
            labelPosition: {default: 'left', type: String},
            value: {required: false}
        }
    };
</script>

Я проверил, что флажок работает с использованием простой нединамической v-модели и без цикла.

Я хочу собрать массив проверенных значений.

В моем примере выше я попытался с этим вычислить, чтобы попытаться связать с vuex, как и с другими полями, но get считается мутацией, потому что он добавляет свойства к объекту по мере его прохождения. Я не знаю как это решить

Vuex:

const state = {        
    searchListType: {}
};

const getters = {
    getSearchListType: function (state) {
        return state.searchListType;
    }
};

const mutations = {
    [Am.SET_SEARCH_TYPES]: (state, types) => {
        state.searchListType = types;
    }
};

Как правильно их связать? Мне нужны значения в vuex, чтобы несколько родственных компонентов могли использовать значения и сохранять их между изменениями страницы.

Кроме того, как правильно заполнить данные? Полагаю, у меня здесь серьезная структурная проблема. FolderList является асинхронным и может загружаться в любой момент, однако обычно он не изменяется после загрузки приложения. Он заполняется родителем, поэтому ребенку просто нужно подождать, пока у него появятся данные, и каждый раз, когда они меняются, по умолчанию отмечать все.

Спасибо


person Trevor    schedule 03.12.2018    source источник
comment
Привет @Trevor! Чтобы прояснить, вы хотите, чтобы значения, которые searchTypeListValues ​​получают () и устанавливают (newValue), влияют на ваш vuex? Вопрос не в моем конце, прошу прощения. Также должны ли ваши ценности присутствовать до монтирования вашего приложения или это может произойти после?   -  person Luay    schedule 03.12.2018
comment
Передайте полный массив компоненту, а внутри пользовательского компонента добавьте новый метод, который изменяет необходимую запись внутри массива и возвращает полный массив.   -  person Art3mix    schedule 03.12.2018
comment
@GrandIQ Привет, я надеюсь получить ключи объекта, которые будут установлены на значения из флажка. Это уже происходит, если я просто использую прямое значение data в соответствии с инструментами разработчика, но не тогда, когда я пытаюсь использовать метод get и set. Вот так: {chk1: true, chk2: false}. Моя единственная другая идея заключалась в том, чтобы оставить его как data и добавить событие щелчка к каждому флажку и использовать его в качестве триггера для вызова фиксации в vuex со значениями.   -  person Trevor    schedule 03.12.2018
comment
Лучший способ поиграть с родительско-дочерними компонентами, используя [props] и vuex, поможет вам произвести мутацию с помощью метода $ this.store для поддержания состояния компонента @TrevorD   -  person saurabh kamble    schedule 03.12.2018
comment
@saurabhkamble Здесь нет проблем с родительским дочерним элементом, и в моем случае это брат-сестра-брат-ребенок-брат-ребенок-ребенок, страшные вещи, лучше с vuex. Мне просто нужно знать, как писать функции получения и установки. Get работает, но набор меня озадачил.   -  person Trevor    schedule 03.12.2018
comment
Я начинаю думать, что это потому, что за подобъектом не следят. Я понятия не имею, как это решить.   -  person Trevor    schedule 03.12.2018


Ответы (1)


Я собирался предложить использовать событие и метод изменения вместо вычисленных get и set, но при тестировании я обнаружил, что v-модель работает нормально без какого-либо дополнительного кода. Ниже приводится приблизительное представление вашего сценария.

Может быть, что-то во взаимодействии с пользовательским компонентом вызывает вашу проблему?

См. Настройка компонентов, использовали ли вы

model: {
  prop: 'checked',
  event: 'change'
},
props: {
  checked: Boolean
},

в компоненте Checkbox?


Vue.component('Checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

new Vue({
  el: '#app',
  data: {
    folderData: [],
    searchTypeList: {}
  },
  created() {
    // Dynamic checkbox simulation
    setTimeout(() => {
      this.folderData = [
        { key: 1 },
        { key: 2 },
        { key: 3 },
      ]
    }, 2000)
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
<ul>
  <li v-if="folderData != null" v-for="folder in folderData">
    <Checkbox :id="folder.key" :name="folder.key" 
      v-model="searchTypeList[folder.key]"></Checkbox>
  </li>
</ul>

{{searchTypeList}}

</div>


Обработка обновлений Vuex

Я думаю, что самый простой способ обрабатывать обновления в магазине - разделить v-model на свойства :checked и @change. Таким образом, ваш элемент управления не пытается напрямую записывать данные в хранилище, но по-прежнему принимает значение напрямую из хранилища и реагирует на изменения в хранилище (как изменения, вызванные вызовом api, так и вызовами $store.commit() этого компонента).

Это соответствующее руководство по обработке форм Vuex.

<ul>
  <li v-if="folderData != null" v-for="folder in folderData">
    <Checkbox :id="folder.key" :name="folder.key" 
      :checked="theList[folder.key]"
      @change="changeChecked(folder.key)"
      ></Checkbox>
  </li>
</ul>

...

computed: {
  ...mapGetters({
    theList: 'getSearchListType'  // Standard getter with get() only
  }),
},
methods: {
  changeChecked(key) {
    this.$store.commit('updateChecked', key)  // Handle details in the mutation
  }
}
person Richard Matsen    schedule 04.12.2018
comment
Спасибо, мне удалось изменить это на стиль get / set и заставить его работать. Я еще не знаю, как получить данные, чтобы установить флажки после вызова базы данных, но если я останусь на этом, я задам другой вопрос. - person Trevor; 04.12.2018
comment
На самом деле это почти работает. Обращение к объекту в get вызывает ошибку «Невозможно изменить внешние мутации». Предположительно потому, что переменная обновляется по мере прохождения цикла. Как мне это исправить? - person Trevor; 04.12.2018
comment
Вычисленный код, который вы сейчас используете в вопросе, явно неверен. Его геттер возвращает себя, а его сеттер устанавливает себя! Результат: vue.js: 1743 RangeError: Превышен максимальный размер стека вызовов в консоли. - person Richard Matsen; 04.12.2018
comment
Извините, это была половина редактирования. Переписал вопрос. Я получал эту ошибку раньше, но исправил ее. Я обнаружил, что мне нужно cloneDeep (), чтобы заставить его перестать жаловаться на мутации вне мутации. Теоретически я могу узнать список папок до загрузки этого компонента (новая опора, вероятно, загружается дважды, один раз пустой и один раз со значениями), как мне предварительно загрузить значения без необходимости клонирования? - person Trevor; 05.12.2018
comment
Я подумал, что, возможно, я мог бы написать это прямо противоположным образом, и использовать локальную переменную данных, и посмотреть, как это будет зафиксировано в vuex. Хотя выглядит одинаково плохо. - person Trevor; 05.12.2018
comment
Я по-прежнему придерживаюсь мнения, что вычисленное получение / установка - неправильный путь. Вам придется взламывать и добавлять дополнительный код, а тестирование будет трудным. Должен быть простой шаблон, у Vue почти всегда есть один для каждого сценария. Я еще посмотрю на него и добавлю код к своему ответу. - person Richard Matsen; 05.12.2018
comment
Если у вас есть другой способ получить результаты в vuex, я полностью за - person Trevor; 05.12.2018
comment
То, как я это сделал, в значительной степени прямо из документации Vue, я уже добавил это в свой ответ. Возможно, вам придется немного его адаптировать, но основная предпосылка - обновить Vuex с помощью события изменения. - person Richard Matsen; 05.12.2018