Двухстороннее связывание данных с использованием синхронизации в компонентах Vue JS - PullRequest
0 голосов
/ 30 июня 2019

У меня есть набор вложенных компонентов Vue JS, для которых я реализую двустороннюю привязку проп, и я не могу работать.

В FoodCat parent component вложено DataTable child 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 в FoodCat parent компонент и экземпляр $ root.

Однако я все еще получаю vue warning, который говорит мне не изменять напрямую реквизит selectedList.

Избегайте прямого изменения свойства, так как значение будет перезаписываться при каждом повторном рендеринге родительского компонента. Вместо этого используйте данные или вычисляемое свойство, основанное на значении реквизита. Мутация опоры: "selectedList"


Почему я все еще получаю это предупреждение? Я неправильно это реализовал?

1 Ответ

2 голосов
/ 30 июня 2019

Это виновник:

v-model="selectedList"

, что эквивалентно

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

Как видите, вы присваиваете selectedList в обработчике для события input.

Когда вы проектируете компоненты таким образом, не связывайте реквизиты с помощью v-model, вместо этого вы должны явно обработать событие input:

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

Кроме того, я не понимаю цель наблюдателя? Реквизит должен меняться только от родителя к ребенку, так зачем вам в ответ отправлять обновление обратно родителю?

...