基于Vuetify Data Table实现动态可编辑列的技术问题
Hey there, let's get your dynamic editable columns working properly. I noticed two main issues in your code that are causing the getEditableColumn function to not run and your slots to fail:
1. Vue 2's Restriction on Dynamic Slot Names
In Vue 2, the value for v-slot:[...] needs to be a reactive property or a template-parsable expression. Calling a function directly inside the slot binding won't trigger during template compilation—that's why your getEditableColumn function never runs. Instead, we can directly use a template string in the slot binding to generate the correct item.columnName format.
2. Hardcoded Data Field
You had props.item.desk hardcoded everywhere in your edit dialog, which means all editable columns would modify the same desk field regardless of which column you're editing. We need to dynamically bind the field using the column value from your loop.
Updated Full Code
Template Section
Here's the fixed template with dynamic slot bindings and proper field references:
<template> <v-card> <v-card-title> {{ title || "" }} <v-col> <v-btn icon color="black" v-if="refresh"> <v-icon>mdi-refresh</v-icon> </v-btn> <v-btn icon color="black" v-if="exportExcel" @click="exportToXlsx"> <v-icon>mdi-microsoft-excel</v-icon> </v-btn> <v-btn icon color="black" v-if="exportPdf"> <v-icon>mdi-file-pdf-box</v-icon> </v-btn> <v-btn icon color="black" v-if="fontSizeControlls"> <v-icon>mdi-format-font-size-decrease</v-icon> </v-btn> <v-btn icon color="black" v-if="fontSizeControlls" @click="logSelected"> <v-icon>mdi-format-font-size-increase</v-icon> </v-btn> </v-col> <v-spacer></v-spacer> <v-col v-if="searchBar"> <v-text-field v-model="search" append-icon="mdi-magnify" label="Search" outlined dense ></v-text-field> </v-col> </v-card-title> <v-data-table @input="(selected) => $emit('selected', selected)" @click:row="rowClickFunction" v-model="selected" :headers="headers" :items="data" :search="search" :show-select="showSelect" :single-select="singleSelect" :height="height" :items-per-page="itemsPerPage" :item-key="itemKey" dense > <!-- Pass through external slots --> <template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope" > <slot :name="slot" v-bind="scope" /> </template> <!-- Dynamically generate editable column slots --> <template v-for="(column, index) in editableColumns" v-slot:[`item.${column}`]="props" :key="index" > <v-edit-dialog :return-value.sync="props.item[column]" @save="save" @cancel="cancel" @open="open" @close="close" > {{ props.item[column] }} <template v-slot:input> <v-text-field v-model="props.item[column]" label="Edit" single-line counter ></v-text-field> </template> </v-edit-dialog> </template> </v-data-table> </v-card> </template>
Script Section
I cleaned up the script, fixed a few typos, and added missing properties to prevent errors:
<script> import { utils, writeFile } from "xlsx"; export default { props: { headers: Array, data: Array, title: String, height: String, itemsPerPage: Number, itemKey: String, searchBar: { type: Boolean, // Fixed typo: was "tpye" default: false }, rowClickFunction: { type: Function, default: () => {}, }, editableColumns: { type: Array, default: () => [] // Added default empty array to avoid loop errors }, refresh: { type: Boolean, default: false, }, exportExcel: { type: Boolean, default: false, }, exportPdf: { type: Boolean, default: false, }, fontSizeControlls: { type: Boolean, default: false, }, singleSelect: { type: Boolean, default: false, }, showSelect: { type: Boolean, default: false, }, xlsxName: { type: String, default: "Sheet.xlsx", }, }, data() { return { search: "", selected: [], dialog: false, snack: false, // Added missing snack state variables snackColor: "", snackText: "" }; }, methods: { exportToXlsx() { const worksheet = utils.json_to_sheet(this.data); const workbook = utils.book_new(); utils.book_append_sheet(workbook, worksheet, "Data"); writeFile(workbook, this.xlsxName); }, logSelected() { console.log(this.selected); }, logRow(row) { console.log(row); console.log(this.selected); }, getSlotName(slot) { return `${slot}.slotName`; }, save() { this.snack = true; this.snackColor = "success"; this.snackText = "Data saved"; }, cancel() { this.snack = true; this.snackColor = "error"; this.snackText = "Canceled"; }, open() { this.snack = true; this.snackColor = "info"; this.snackText = "Dialog opened"; }, close() { console.log("Dialog closed"); }, }, }; </script>
Key Fixes & Improvements
- Dynamic Slot Names: Replaced the function call with a template string
v-slot:[item.${column}]which Vue 2 can properly parse - Dynamic Field Binding: Changed all instances of
props.item.desktoprops.item[column]so each editable column targets the correct field - Typo Fix: Corrected
tpyetotypein thesearchBarprop - Default Value: Added a default empty array for
editableColumnsto prevent errors when the prop isn't passed - Missing State: Added
snack,snackColor, andsnackTextto the data object—these were referenced in your methods but weren't defined, which would have caused runtime errors
Now when you pass an array like ['name', 'age', 'desk'] to the editableColumns prop, those columns will automatically become editable with the correct field bindings.
内容的提问来源于stack exchange,提问作者Vlad Tanase

