我正在尝试向我的 DataTable 中的行添加链接/按钮,单击后会将您带到 vue 路由。当然,我可以简单地添加一个带有
<a>
的普通 href="/item/${id}"
链接。但这会绕过路由器并导致整个页面重新加载
我想出了两个解决方案,都依赖于设置
onclick
来执行一些代码:
在
route1
中,我在vueRoute
对象上附加了一个名为window
的函数,它只是对vue方法route1
的引用。现在可以从任何地方调用此功能
在
route2
中,我有 onclick
触发 CustomEvent
,我有一个事件侦听器设置为由 route2
vue 方法处理
我对这两种解决方案都不满意。尽管它们有效,但它们看起来非常“hacky”。我应该做什么呢?
// datatables column definitions
columns: [
// route1
{
data: 'id',
render: function (dt) {
return `<a href="#" onclick="(function(){vueRoute('${dt}');})()">route1</a>`;
},
},
// route2
{
data: 'id',
render: function (dt) {
return `<a href="#" onclick="(function(){document.body.dispatchEvent(
new CustomEvent('clickedEdit', {detail : ${dt} }));})() ">route2</a>`;
},
},
],
// vue methods
methods: {
route1(id) {
this.router.push('/item/' + id);
},
route2(evt) {
this.router.push('/item/' + evt.detail);
},
},
// on mount, set up route1 and route2
mounted() {
window.vueRoute = this.route1;
document.body.addEventListener('clickedEdit', this.route2);
},
您可以在 DataTable 对象上设置
@click
处理程序:
<DataTable
...
@click="resolveRouteFromClick($event)"
>
该事件将是一个常规的点击事件,因此我们需要一个可识别的数据集属性 id:
{
data: 'id',
render: function (idValue) {
return `<a href="#" data-item-id="${idValue}">route3</a>`;
},
},
点击处理程序只需确保可以检索到 id,确保点击来自链接:
resolveRouteFromClick(e){
const itemId = e.target.dataset.itemId
if (!itemId) {
return
}
e.preventDefault()
this.router.push('/item/' + itemId);
}
这是更新后的stackblitz
有趣的是,问题甚至不是 Vue 和 jQuery 之间的连接,而是 DataTables 的工作方式,特别是没有单击单元格或单击行的事件,并且渲染器只能返回 HTML字符串。
不过这并不奇怪,jQuery 的工作方式就是这样,数据、视图和行为之间并没有内在的联系,但是您可以按照您认为合适的方式将它们联系起来。所以没有我们可以搭载的单元格单击事件,因为在 jQuery 中,你自己编写它或者它不存在。
在 jQuery 中设置处理程序的最有效方法是使用目标过滤器在表上设置事件:
$('.datatable').on('click', 'a[data-item-id]', function(){...})
优点是:
和上面的解决方案完全一样,只是在Vue中设置了事件,我们必须自己编写目标过滤器,这是没有问题的。
所以我认为这是使其在 Vue 中工作的最有效方法,无论是代码量(可维护性)还是性能。
我不会说这是最好的解决方案,但在我看来,据我所知,没有真正的“最佳”解决方案,因为数据表使用的是 jQuery 而你在这里使用的是 vue(DOM 控件有点不一致).通过快速搜索,我发现没有办法 在数据表的
render
属性中渲染一个 vue 组件。
所以我的想法是不同的,它简化渲染功能,并使用
drawCallback
添加事件监听器并使用事件委托来附加监听器。
来自文档:
DataTables 每次执行绘制时调用的函数。
...您可能想将事件分配给新创建的元素
data() {
return {
...
columns: [
...
{
data: 'id',
render: function (dt) {
return `<a href="#" class="route3" data-item-id='${dt}'>route3</a>`;
},
},
],
};
},
methods: {
...
drawCallback(settings) {
const tableElm = document.getElementById(settings.sInstance);
tableElm.addEventListener('click', (e) => {
const id = e.target.dataset.itemId;
if (id) {
this.router.push('/item/' + id);
}
});
},
},
...
<template>
...
<DataTable
:data="data"
:columns="columns"
:options="{
drawCallback: drawCallback,
}"
class="display"
width="100%"
>
...
</template>
所以我简单地将
data-item-id
附加到每个链接,然后使用父级的点击事件来进行路由器推送(事件委托)。我知道这不是最好的答案,但也许这可能是您的另一种选择。
如果你有兴趣,这就是分叉的沙箱。
你想避免 hacky 解决方案是可以理解的。这是使用 Vue 指令和更以 Vue 为中心的方法来处理这种情况的更好方法。
首先,您需要使用 v-html 指令来呈现 DataTable 列的原始 HTML。
接下来,使用 v-on 指令(或简写 @click)将点击事件绑定到链接,并以行数据作为参数调用 Vue 方法。
以下是修改代码的方法:
columns: [
{
data: 'id',
render: function (id) {
return `<a class="route1-link" data-id="${id}">route1</a>`;
},
},
{
data: 'id',
render: function (id) {
return `<a class="route2-link" data-id="${id}">route2</a>`;
},
},
],
mounted() {
this.$nextTick(() => {
// Attach event listeners to the route1-link and route2-link elements
this.$el.querySelectorAll('.route1-link').forEach((link) => {
link.addEventListener('click', (event) => {
event.preventDefault();
this.route1(event.target.dataset.id);
});
});
this.$el.querySelectorAll('.route2-link').forEach((link) => {
link.addEventListener('click', (event) => {
event.preventDefault();
this.route2(event.target.dataset.id);
});
});
});
},
通过这些更改,您现在可以使用 Vue 的内置功能来处理路由和点击事件,从而使代码更易于维护并且更不容易出错。