Vue - обновление данных в пользовательском компоненте таблицы начальной загрузки

Я пытаюсь создать специальный компонент в Vue 2.0, который расширяет существующие функциональные возможности библиотеки Bootstrap Vue <b-table>. В основном он работает так, как мне хотелось бы, за исключением того, что функции removeRow и resetData, определенные в index.jsp, работают не так, как мне хотелось бы.

removeRow визуально удаляет строку и удаляет ее из свойства data, но строка снова появляется после следующего взаимодействия (сортировка, фильтрация и т. д.). Так что это не совсем то, что нужно обновлять. Я пытаюсь использовать v-модель как неглубокую копию элементов, чтобы я мог удалять из нее, не затрагивая исходный набор данных, но мне просто что-то не хватает.

resetData правильно устанавливает данные в таблице, но не повторно отображает таблицу, поэтому вы не можете видеть повторно добавленные строки, пока не выполните отдельное взаимодействие (сортировка, фильтр и т. д.), и в этом случае они появляются снова.

Итак, я знаю, что я в некоторой степени близок, но был бы очень признателен за любое понимание того, как заставить это работать правильно и как я мог бы улучшить любую часть этого компонента.

OreillyTable.vue.js

const OreillyTable =  {
inheritAttrs: false,
data: function () {
    return {
        filter: null,
        sortDesc: false,
        hideEmpty: false,
        isBusy: false,
        currentPage: 1,
        data: null
    }
},
mounted: function () {
    let filtered = this.slots.filter(function(value, index, arr){
        return value.customRender;
    });
    this.slots = filtered;
},
methods: {
    oreillyTableSort (a, b, key) {
        if (a[key] === null || b[key] === null) {
            return a[key] === null && b[key] !== null ? -1 : (a[key] !== null && b[key] === null ? 1 : 0);
        } else if (typeof a[key] === 'number' && typeof b[key] === 'number') {
            // If both compared fields are native numbers
            return a[key] < b[key] ? -1 : (a[key] > b[key] ? 1 : 0)
        } else {
            // Stringify the field data and use String.localeCompare
            return this.toString(a[key]).localeCompare(this.toString(b[key]), undefined, {
                numeric: true
            });
        }
    },

    toString (val) {
        return typeof val !== "undefined" && val != null ? val.toString() : '';
    },

    oreillyFilter (filteredItems) {
        this.totalRows = filteredItems.length;
        this.currentPage = 1;
    }
},
props: {
    fields: {
        type: Array
    },
    items: {
        type: Array,
        required: true
    },
    hideEmpty: {
        type: Boolean
    },
    filterPlaceholder: {
        type: String,
        default: "Filter"
    },
    sortFunction: {
        type: Function,
        default: null
    },
    filterFunction: {
        type: Function,
        default: null
    },
    slots: {
        type: Array
    },
    sortBy: {
        type: String,
        default: null
    },
    perPage: {
        type: Number,
        default: 10
    },
    value: {

    }
},
template: `<div :class="{ 'no-events' : isBusy }">
            <b-row>
                <b-col md="12">
                    <b-form-group class="mb-2 col-md-3 float-right pr-0">
                        <b-input-group>
                            <b-form-input v-model="filter" :placeholder="filterPlaceholder" class="form-control" />
                        </b-input-group>
                    </b-form-group>
                </b-col>
            </b-row>
            <div class="position-relative">
                <div v-if="isBusy" class="loader"></div>
                <b-table stacked="md" outlined responsive striped hover
                    v-bind="$attrs"
                    v-model="data"
                    :show-empty="!hideEmpty"
                    :items="items"
                    :fields="fields"
                    :no-provider-sorting="true" 
                    :no-sort-reset="true"
                    :filter="filter"
                    :sort-by.sync="sortBy" 
                    :sort-desc.sync="sortDesc" 
                    :sort-compare="sortFunction === null ? this.oreillyTableSort : sortFunction" 
                    :busy.sync="isBusy"
                    :current-page="currentPage"
                    :per-page="perPage"
                    @filtered="filterFunction === null ? this.oreillyFilter : filterFunction">

                    <template :slot="slot.key" slot-scope="row" v-for="slot in slots">
                        <slot :name="slot.key" :data="row"></slot>
                    </template>
                </b-table>

                <b-row v-if="items.length > perPage">
                    <b-col sm="12">
                        <b-pagination size="md" :total-rows="items.length" v-model="currentPage" :per-page="perPage"></b-pagination>
                    </b-col>
                </b-row>
            </div>
        </div>`
};

index.jsp

<script>
Vue.use(window.vuelidate.default);

Vue.component('oreilly-table', OreillyTable);

const dashboardItems = [
    { id: 12, firstName: "John", lastName: "Adams", tmNumber: "588999", team: "Corporate", flapjackCount: 4, enrollDate: "2018-11-05" },
    { id: 13, firstName: "George", lastName: "Washington", tmNumber: "422111", team: "America", flapjackCount: 28, enrollDate: "2018-10-01" },
    { id: 14, firstName: "Abraham", lastName: "Lincoln", tmNumber: "358789", team: "America", flapjackCount: 16, enrollDate: "2017-09-02" },
    { id: 15, firstName: "Jimmy", lastName: "Carter", tmNumber: "225763", team: "Core", flapjackCount: 9, enrollDate: "2018-03-02" },
    { id: 16, firstName: "Thomas", lastName: "Jefferson", tmNumber: "169796", team: "Core", flapjackCount: 14, enrollDate: "2018-05-01" }
];

const Dashboard = {
    template: `<jsp:include page="dashboard.jsp"/>`,
    data: function(){
        return {            
            notification: {
                text: "The Great Flapjack Contest will be held on December 25, 2018.",
                variant: "primary",
                timer: true
            },
            fields: [
                { key: "name", label: "Name", sortable: true, customRender: true },
                { key: "team", label: "Team", sortable: true },
                { key: "enrollDate", label: "Enrollment Date", sortable: true, formatter: (value) => {return new Date(value).toLocaleDateString();} },
                { key: "flapjackCount", sortable: true },
                { key: "id", label: "", 'class': 'text-center', customRender: true }
            ]
        }
    },
    methods: {
        removeRow: function(id) {
            this.$refs.table.isBusy = true;
            setTimeout(() => { console.log("Ajax Request Here"); this.$refs.table.isBusy = false; }, 1000);
            const index = this.$refs.table.data.findIndex(item => item.id === id) 
            if (~index) 
                this.$refs.table.data.splice(index, 1)  
        },
        resetData: function() {
            this.$refs.table.data = dashboardItems;
        }
    }
};

const router = new VueRouter({
    mode: 'history',
    base: "/ProjectTemplate/flapjack",
    routes: [
        { path: '/enroll', component: Enroll },
        { path: '/', component: Dashboard },
        { path: '/404', component: NotFound },  
        { path: '*', redirect: '/404' }
    ]
});

new Vue({router}).$mount('#app');

dashboard.jsp

<compress:html>
<div>
    <oreilly-table ref="table" :items="dashboardItems" :slots="fields" :fields="fields">
        <template slot="name" slot-scope="row">
            {{ row.data.item.firstName }} {{ row.data.item.lastName }} ({{ row.data.item.tmNumber }})
        </template>
        <template slot="id" slot-scope="row">
            <a href="#" @click.prevent="removeRow(row.data.item.id)">Remove</a>
        </template>
    </oreilly-table>
    <footer class="footer position-sticky fixed-bottom bg-light">
        <div class="container text-center">
            <router-link tag="button" class="btn btn-outline-secondary" id="button" to="/enroll">Enroll</router-link>
            &emsp;
            <b-button @click.prevent="resetData" size="md" variant="outline-danger">Reset</b-button>
        </div>
    </footer>
</div>


person dukedevil294    schedule 28.11.2018    source источник


Ответы (1)


Я попытался воспроизвести вашу проблему на простом примере (см .: https://codesandbox.io/s/m30wqm0xk8?module=%2Fsrc%2Fcomponents%2FGridTest.vue), и я столкнулся с той же проблемой, что и вы. Как уже говорили другие, я согласен с тем, что самый простой способ сбросить исходные данные - это сделать копию. Я написал два метода для удаления и сброса данных.

methods: {
    removeRow(id) {
      const index = this.records.findIndex(item => item.index === id);

      this.records.splice(index, 1);
    },
    resetData() {
      this.records = this.copyOfOrigin.slice(0);
    }
  }

При монтировании я выполняю функцию, которая копирует данные. Это делается с помощью slice, потому что в противном случае он делает только ссылку на исходный массив (обратите внимание, что обычно JS передается по значению, но, как указано в документации vue с объектами, и, следовательно, внутри vue он передается по ссылке (см. : Vue docs выделите красный текст)).

mounted: function() {
    this.copyOfOrigin = this.records.slice(0);
  } 

Теперь вы можете просто удалить запись, но также сбросить все данные.

ПОЛНЫЙ ДЕМО

Я надеюсь, что это решит вашу проблему, и если у вас есть какие-либо вопросы, не стесняйтесь спрашивать.

person Kimberley    schedule 30.11.2018
comment
Привет, Кимберли, не могли бы вы разобраться в моем коде? У меня очень похожая проблема, я пробовал ваш метод безрезультатно. Очень ценю вашу помощь. stackoverflow.com/ questions / 55920207 / - person NewProgrammer; 30.04.2019
comment
@NewProgrammer, я посмотрю, смогу ли я помочь :) - person Kimberley; 01.05.2019