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

Checkbox е персонализиран компонент с квадратче за отметка в плъзгащ се стил, неговият 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 ​​get() и set(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 и set. Get работи, но комплектът ме озадачи.   -  person Trevor    schedule 03.12.2018
comment
Започвам да си мисля, че е защото подобектът не се наблюдава. Нямам идея как да реша това.   -  person Trevor    schedule 03.12.2018


Отговори (1)


Щях да предложа използването на събитие и метод за промяна вместо изчислено получаване и настройка, но при тестване открих, че 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 Form Handling.

<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
Съжаляваме, това беше половин редакция. Пренаписах въпроса. Получих тази грешка преди, но я поправих. Това, което откривам, е, че трябва да клонирам Deep(), за да го накарам да спре да се оплаква от мутиране извън мутация. На теория мога да знам folderList преди този компонент да се зареди (новият проп вероятно се зарежда два пъти, веднъж празен и веднъж със стойности), как да заредя предварително стойностите, без да се налага да клонирам? - person Trevor; 05.12.2018
comment
Мислех си, че вероятно бих могъл да напиша и това по точно обратния начин и да използвам локална променлива за данни и да я гледам да се ангажира с vuex. Все пак изглежда еднакво зле. - person Trevor; 05.12.2018
comment
Все още съм на мнение, че изчисленото get/set е грешният начин. Трябва да хакнете и да добавите допълнителен код, а тестването ще бъде трудно. Трябва да има прост модел, Vue почти винаги има един за всеки сценарий. Ще го разгледам още малко и ще добавя някакъв код към отговора си. - person Richard Matsen; 05.12.2018
comment
Ако имате различен начин за получаване на резултатите във vuex, аз съм за него - person Trevor; 05.12.2018
comment
Начинът, по който бих го направил, е почти направо от документите на Vue, вече го добавих към отговора си. Може да се наложи да го адаптирате малко, но основната предпоставка е да актуализирате Vuex чрез събитие за промяна. - person Richard Matsen; 05.12.2018