I'm trying to add a link/button to a row in a DataTable that when clicked will take you to a vue route. Of course, I could simply add a plain <a>
link containing href="/item/${id}"
. But this bypasses the router and causes the entire page to reload
I came up with two solutions, both of which rely on setting onclick
to execute some code:
In route1
, I attach a function called vueRoute
to the window
object, which is just a callback to the vue method A reference to route1
. This function can now be called from anywhere
In route2
, I have onclick
trigger the CustomEvent
, and I set up an event listener to be triggered by route2
vue method handles it
I'm not satisfied with either solution. Although they work, they look "hacky". what should I do?
// 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); },
A working example can be found here: https://stackblitz.com/edit/datatables-net-vue3-simple-mgdhf1?file=src/components/ListView.vue
EDIT: What I actually ended up doing was switching to a headless table component (TanStack), which works better in Vue than DataTables. But if you need to use DataTables, the solution posted by @Damzaky and @Moritz Ringler below will work
P粉5881526362024-03-28 17:27:38
I wouldn't say this is the best solution, but in my opinion, as far as I know, there is no real "best" solution, because the datatable uses jQuery and you are using vue here (DOM control Very friendly) inconsistent). After a quick search I didn't find a way
Render a vue component within the render
property of the data table.
So my idea is different, it simplifies the render function and uses drawCallback
to add the event listener and uses an event delegate to attach the listener.
From Documentation:
data() { return { ... columns: [ ... { data: 'id', render: function (dt) { return `route3`; }, }, ], }; }, 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); } }); }, }, ... ......
So I just append the data-item-id
to each link and then use the parent's click event to do the router push (event delegation). I know this isn't the best answer, but maybe this could be another option for you.
This If you are interested, this is the forked sandbox.
P粉1342887942024-03-28 16:25:12
You can set a @click
handler on the DataTable object:
The event will be a regular click event, so we need an identifiable id dataset attribute:
{ data: 'id', render: function (idValue) { return `route3`; }, },
The click handler just needs to make sure that the id can be retrieved and that the click came from a link:
resolveRouteFromClick(e){ const itemId = e.target.dataset.itemId if (!itemId) { return } e.preventDefault() this.router.push('/item/' + itemId); }
Here is the updated Stack Blitz
Interestingly, just as background, the problem is not even the connection between Vue and jQuery, but the way DataTables work, specifically there are no events for cell click or row click, and the renderer can only return HTML String.
This is not surprising, this is how jQuery works, where data, views, and behaviors are not intrinsically linked, but you can connect them how you see fit. So we don't have a cell click event to exploit because in jQuery you write it yourself or it doesn't exist.
The most efficient way to set a handler in jQuery is to set the event on a table using a target filter:
$('.datatable').on('click', 'a[data-item-id]', function(){...})
The advantage is:
The Vue solution above does the exact same thing, just without jQuery.
So I think this is the most efficient way to make it work in Vue, both in terms of code size (maintainability) and performance.
Please note that since DataTable requires jQuery to be globally accessible, it can also be used in your Vue component, and you can put the given jQuery statement into a mounted
hook, which you can do in Which accesses the Vue router in the callback. So instead of putting the router in global scope to use it on the jQuery side, you can use an object that already exists on the Vue side.
Still, since there's no need to mix jQuery into your Vue code, I would avoid it and use the Vue-only solution above.