VueJs 和 Vuex,如何将 v-model 附加到动态字段

VueJs and Vuex, how to attach v-model to dynamic fields

我有一系列由数据库生成的复选框。数据库调用通常不会在页面加载之前完成。

这是我的 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-model是这样的

<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 模型且没有循环验证了复选框是否正常工作。

我想收集一组检查值。

在我上面的例子中,我尝试使用这个计算来尝试 link 到 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;
    }
};

link这些的正确方法是什么?我需要 vuex 中的值,以便多个同级组件可以使用这些值并在页面更改之间存储它们。

另外,预填数据的正确方法是什么?我假设我这里有一个主要的结构问题。 FolderList 是异步的,可以随时加载,但在应用程序加载后它通常不会更改。由parent填充,child只需要等待它有数据,每次变化,默认勾选。

谢谢

我打算建议使用更改事件和方法而不是计算的获取和设置,但在测试时我发现 v-model 工作正常,无需任何额外代码。以下是您的场景的粗略估计。

也许自定义组件交互中的某些内容导致了您的问题?

Ref Customizing Components,你用过吗

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

在复选框组件中?


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