在Vue JS组件中使用同步的双向prop数据绑定

时间:2019-06-30 00:33:03

标签: javascript vue.js vuejs2 vue-component vuetify.js

我有一组嵌套的Vue JS组件,我无法在这些组件上实现双向prop绑定。

DataTable child component 中嵌套了一个 FoodCat parent component 。我在selected内有一个名为 FoodCat parent component 的属性,该属性通过道具传递给了DataTable。我想使用.sync方法在道具上实现双向绑定。

FoodCats.vue-父组件:

<template>
    <admin-data-table
        :dataTable="dataTable"
        :modelName="modelName"
        :collection="collection"
        :tblShowFields="tblShowFields"
        :selectedList.sync="selected"
    ></admin-data-table>
</template>

<script>
    import { MessageBox } from '../../MessageBox.js';
    import { crudFunctionsMixin } from './mixins/crud-functions.js';

    Vue.component('admin-data-table', require('../components/Admin/DataTable').default);

    export default {
        mixins: [ crudFunctionsMixin ],
        data() {
            return {
                model: "food-cat",
                modelName: "Food Category",
                modelNamePlural: "Food Categories",
                form: {
                    inputs: {
                        id: {
                            val: '', save: true,
                            hide: true
                        },
                        title: {
                            val: '', save: true, add: true,
                            gridSize: 12,
                            icon: 'business',
                            placeholder: 'Title'
                        },
                        slug: {
                            val: '', save: true, add: true
                        }
                    },
                    titleText: "Add Food Category",
                    errors: false
                },
                formSearch: {
                    inputs: {
                        keywords: {
                            show: true,
                            val: "",
                            icon: "keyboard",
                            placeholder: "Keywords"
                        }
                    }
                },
                toolbar: {
                    btns: [],
                    menuItems: []
                },
                dataTable: {
                    headers: [
                        { text: 'ID', value: 'id', sortable: true },
                        { text: 'Title', value: 'title', sortable: true },
                        { text: 'Slug', value: 'slug', sortable: true },
                        { sortable: false }
                    ],
                    pagination: {
                        sortBy: 'title'
                    },
                    rowButtons: []
                }
            }
        },
        watch: {
            selected: function(newSelectedList) {
                this.$root.selected = newSelectedList;
            }
        }
    }
</script>

DataTable.vue-子组件:

<template>
    <v-flex xs12>
        <v-progress-linear :indeterminate="true" :height="3" color="#c79121" :active="dataTable.loadingVal" class="mb-0 mt-5"></v-progress-linear>
        <v-data-table :ref="modelName + 'Table'" v-model="selectedList" :headers="dataTable.headers" :items="collection" :pagination.sync="dataTable.pagination" select-all item-key="id" class="elevation-1" >
            <template v-slot:headers="props">
                <tr>
                    <th><v-checkbox :input-value="props.all" color="#c79121" :indeterminate="props.indeterminate" primary hide-details @click.stop="toggleAllSelected"></v-checkbox></th>
                    <th v-for="header in props.headers" :key="header.text"
                        :class="['column sortable', dataTable.pagination.descending ? 'desc' : 'asc', header.value === dataTable.pagination.sortBy ? 'active' : '']"
                        @click="changeSort(header.value)">
                        <v-icon small>arrow_upward</v-icon>
                        {{ header.text }}
                    </th>
                </tr>
            </template>
            <template v-slot:items="props">
                <tr :active="props.selected">
                    <td class="text-center align-middle" @click="props.selected = !props.selected">
                        <v-checkbox :input-value="props.selected" primary hide-details color="#c79121"></v-checkbox>
                    </td>
                    <td v-for="(field, key) in props.item" v-if="tblShowFields.includes(key)">{{ field }}</td>
                    <td class="text-right align-middle">
                        <v-btn title="Edit" color="primary" fab small @click="edit(props.item.id)"><v-icon>edit</v-icon></v-btn>
                        <v-btn title="Delete" color="error" fab small class="text-white" @click="remove(props.item.id)"><v-icon>delete_outline</v-icon></v-btn>
                    </td>
                </tr>
            </template>
            <template slot="no-data">
                <p class="text-xs-center">No Data</p>
            </template>
        </v-data-table>
    </v-flex>
</template>

<script>
    export default {
        name: "admin-data-table",
        props: [
            'dataTable',
            'collection',
            'modelName',
            'collection',
            'selectedList',
            'tblShowFields'
        ],
        watch: {
            selectedList: function(newList) {
                this.$emit('update:selectedList', newList);

            }
        }
    }
</script>

当前selectedList组件内的 DataTable child 对象与表格复选框一起正常工作,并且更改已正确同步到 selected < / strong>对象(位于FoodCat parent组件中,并指向$ root实例。

但是我仍然收到vue warning的提示,告诉我不要直接修改selectedList道具。

  

避免直接更改prop,因为每当父组件重新渲染时,该值都会被覆盖。而是使用基于属性值的数据或计算属性。道具被突变:“ selectedList”


为什么我仍然收到此警告?我实施不正确吗?

1 个答案:

答案 0 :(得分:2)

这是罪魁祸首:

v-model="selectedList"

等效于

:value="selectedList" @input="selectedList = $event"

如您所见,您正在为selectedList事件在处理程序中分配给input

以这种方式设计组件时,请确保不要将道具与v-model绑定,而必须显式处理input事件:

:value="selectedList" @input="$emit('update:selectedList', $event)"

我也不了解观察者的目的?该道具只能从父级更改为子级,那么为什么您需要将更新发送回父级呢?