【问题标题】:Two way prop data-binding using sync in Vue JS components在 Vue JS 组件中使用同步的两种方式 prop 数据绑定
【发布时间】:2019-11-11 06:49:31
【问题描述】:

我有一组嵌套的 Vue JS 组件,我正在实现双向 prop 绑定,但我无法开始工作。

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

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>

目前,DataTable child 组件内的 selectedList 对象与表格复选框正常工作,并且更改正在正确同步到 selected 对象中FoodCat parent 组件,以及 $root 实例。

但是我仍然收到 vue warning 告诉我不要直接修改 selectedList 属性。

避免直接改变 prop,因为每当父组件重新渲染时,该值将被覆盖。相反,使用基于道具值的数据或计算属性。正在变异的道具:“selectedList”


为什么我仍然收到此警告?我是否执行不正确?

【问题讨论】:

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


    【解决方案1】:

    这是罪魁祸首:

    v-model="selectedList"
    

    相当于

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

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

    当你以这种方式设计组件时,一定不要将 props 绑定到v-model,而是必须显式处理input 事件:

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

    我也不明白观察者的目的? prop 应该只从父级到子级更改,那么为什么需要向父级发出更新作为响应?

    【讨论】:

      猜你喜欢
      • 2018-08-05
      • 2014-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-16
      相关资源
      最近更新 更多