search

Home  >  Q&A  >  body text

Enhance data tables with Vue Router

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:

  1. 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

  2. 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粉818088880P粉818088880245 days ago377

reply all(2)I'll reply

  • P粉588152636

    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.

    reply
    0
  • P粉134288794

    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:

    • Only one event occurs for all rows
    • No need to add events when changing pages or re-rendering
    • Keep code in rendering functions to a minimum

    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.

    reply
    0
  • Cancelreply