Vue3组合式API自定义DataTable:模板按钮无法绑定函数求助
问题原因
你直接通过字符串模板渲染的HTML内容,Vue不会对其进行编译处理,所以@click这类Vue指令根本不会被识别,自然点击后没有响应。
解决方案
推荐优先使用Vue官方的组件自定义内容方案,以下是几种可行的实现方式:
方案1:作用域插槽(Scoped Slots)
这是最符合Vue设计思路的方式,灵活性和可维护性最强。
第一步:修改DataTable组件内部逻辑
<template> <table> <thead> <tr> <th v-for="col in config.columns" :key="col.field">{{ col.title }}</th> </tr> </thead> <tbody> <tr v-for="row in tableData" :key="row.id"> <td v-for="col in config.columns" :key="col.field"> <!-- 若列配置了slot名称,渲染对应插槽并传递当前行数据 --> <slot v-if="col.slot" :name="col.slot" :row="row"></slot> <!-- 否则渲染字段默认值 --> <span v-else>{{ row[col.field] }}</span> </td> </tr> </tbody> </table> </template> <script setup> import { ref, onMounted } from 'vue' const props = defineProps({ config: Object }) const tableData = ref([]) // 你的远程数据获取逻辑(保留原有实现即可) onMounted(async () => { if (props.config.data.type === 'remote') { const res = await fetch(props.config.data.url) tableData.value = await res.json() } }) </script>
第二步:父组件中通过插槽传递按钮
<template> <DataTable :config="tableConfig"> <!-- 对应actions列的插槽,row是子组件传递的当前行数据 --> <template #actions="{ row }"> <button @click="deleteRow(row.id)">delete</button> </template> </DataTable> </template> <script setup> import { ref } from 'vue' import DataTable from './DataTable.vue' const tableConfig = ref({ data: { type: 'remote', url: '/api/categories/get' }, columns: [ { field: 'id', title: '#' }, { field: 'name', title: 'Name' }, { field: 'order', title: 'Placement' }, { field: 'actions', title: 'Actions', slot: 'actions' } // 指定插槽名称 ] }) const deleteRow = (id) => { // 这里写你的删除逻辑 console.log('要删除的ID:', id) } </script>
方案2:渲染函数(h函数)
适合需要动态生成复杂DOM结构的场景,直接返回Vue可识别的虚拟节点。
修改DataTable组件内部逻辑
<template> <table> <!-- 表头部分不变 --> <thead> <tr> <th v-for="col in config.columns" :key="col.field">{{ col.title }}</th> </tr> </thead> <tbody> <tr v-for="row in tableData" :key="row.id"> <td v-for="col in config.columns" :key="col.field"> <!-- 调用配置中的render函数生成虚拟节点 --> <component v-if="col.render" :is="col.render(row)"></component> <span v-else>{{ row[col.field] }}</span> </td> </tr> </tbody> </table> </template> <script setup> // 保留原有数据获取等逻辑即可 </script>
父组件配置使用
<template> <DataTable :config="tableConfig"></DataTable> </template> <script setup> import { ref, h } from 'vue' import DataTable from './DataTable.vue' const deleteRow = (id) => { console.log('要删除的ID:', id) } const tableConfig = ref({ data: { type: 'remote', url: '/api/categories/get' }, columns: [ { field: 'id', title: '#' }, { field: 'name', title: 'Name' }, { field: 'order', title: 'Placement' }, { field: 'actions', title: 'Actions', // 用h函数生成按钮节点,绑定点击事件 render: (row) => h('button', { onClick: () => deleteRow(row.id) }, 'delete') } ] }) </script>
方案3:事件委托(兜底方案)
如果坚持要用字符串模板渲染,可通过父元素的事件委托来触发函数,但不推荐长期使用,不利于代码维护。
修改DataTable组件内部逻辑
<template> <!-- 在table上绑定点击事件,统一处理 --> <table @click="handleTableClick"> <!-- 表头部分不变 --> <thead> <tr> <th v-for="col in config.columns" :key="col.field">{{ col.title }}</th> </tr> </thead> <tbody> <tr v-for="row in tableData" :key="row.id"> <td v-for="col in config.columns" :key="col.field"> <div v-if="col.template" v-html="col.template(row)"></div> <span v-else>{{ row[col.field] }}</span> </td> </tr> </tbody> </table> </template> <script setup> import { ref, onMounted, defineProps, defineEmits } from 'vue' const props = defineProps({ config: Object }) const emit = defineEmits(['delete-row']) const tableData = ref([]) // 数据获取逻辑保留原有实现 onMounted(async () => { if (props.config.data.type === 'remote') { const res = await fetch(props.config.data.url) tableData.value = await res.json() } }) // 事件委托处理点击 const handleTableClick = (e) => { if (e.target.tagName === 'BUTTON' && e.target.dataset.action === 'delete') { const id = parseInt(e.target.dataset.id) emit('delete-row', id) } } </script>
父组件配置使用
<template> <DataTable :config="tableConfig" @delete-row="deleteRow"></DataTable> </template> <script setup> import { ref } from 'vue' import DataTable from './DataTable.vue' const deleteRow = (id) => { console.log('要删除的ID:', id) } const tableConfig = ref({ data: { type: 'remote', url: '/api/categories/get' }, columns: [ { field: 'id', title: '#' }, { field: 'name', title: 'Name' }, { field: 'order', title: 'Placement' }, { field: 'actions', title: 'Actions', // 给按钮加自定义属性,用于事件委托识别 template: (row) => `<button data-action="delete" data-id="${row.id}">delete</button>` } ] }) </script>
内容的提问来源于stack exchange,提问作者ProtoN
相关产品推荐
相关产品推荐

