renanleandrof
renanleandrof

Reputation: 7017

How to create router-link programmatically on html render using Datatables.net + Vue.js?

I have a Datatables.net jquery plugin as a vue component:

DatatablesCGU:

<template>
    <table v-bind="$props" ref="tableElement" class="table table-striped table-hover table-bordered">
        <slot></slot>
    </table>
</template>
<script>

    import $ from 'jquery';
    // Datatables
    require('datatables.net-bs');
    require('datatables.net-bs4/js/dataTables.bootstrap4.js');
    require('datatables.net-buttons');
    require('datatables.net-buttons-bs');
    require('datatables.net-responsive');
    require('datatables.net-responsive-bs');
    require('datatables.net-responsive-bs/css/responsive.bootstrap.css');
    require('datatables.net-buttons/js/buttons.colVis.js'); // Column visibility
    require('datatables.net-buttons/js/buttons.html5.js'); // HTML 5 file export
    require('datatables.net-buttons/js/buttons.flash.js'); // Flash file export
    require('datatables.net-buttons/js/buttons.print.js'); // Print view button
    require('datatables.net-keytable');
    require('datatables.net-keytable-bs/css/keyTable.bootstrap.css');
    require('datatables.net-select');
    require('jszip/dist/jszip.js');
    require('pdfmake/build/pdfmake.js');
    require('pdfmake/build/vfs_fonts.js');

    //Evita o alert chato do datatables em caso de erro
    $.fn.dataTable.ext.errMode = function ( settings, helpPage, message ) {
        console.error(message);
    };
    /**
     * Wrapper component for dataTable plugin
     * Only DOM child elements, componets are not supported (e.g. <Table>)
     */
    export default {
        name: 'DatatableCGU',
        props: {
            /** datatables options object */
            options: { type: Function, "default": ()=>{} },
            /** callback that receives the datatable instance as param */
            dtInstance: Function
        },

        data(){
            return { datatables : null}
        },

        mounted() {
            const dtInstance = $(this.$refs.tableElement).DataTable(this.options());
            this.datatables = dtInstance;
            if (this.dtInstance) {
                this.dtInstance(dtInstance);
            }

            this.$root.$on('filtrar', this.refresh);
        },

        destroyed() {
            $(this.$refs.tableElement).DataTable({destroy: true});
        },

        methods: {
            refresh(filtros) {
               this.datatables.ajax.reload();
            }
        }

    }
</script>

On another component, i use this passing a datatables options with some custom renders on columns properties:

...
methods: {
            getOptions(){
                let options = this.getDefaultOptions();
                options.ajax.url = "/api/auth/usuarios";
                options.filtrador = this.filtrador;
                options.columns = [
                    this.colunaDeSelecao(this.modoPopup)
                    ,{name: "cpf", data: "cpf", title: "CPF"}
                    ,{name: "nome", data: "nome",  title: "Nome"}
                    ,{name: "email", data: "email",  title: "E-mail"}
                    ,{name: "id", data: "id", title: "Ações", visible: !(this.modoPopup), sortable:false, className:"dt-center", width: "200px", render: function(data, type, row) {
                        return `<span class='btn-group btn-group-sm'>
                                    <button id='btnAlternar__${data}' data-id='${data}' class='btn btn-${row.ativo?"danger":"success"}' data-toggle='tooltip' title='${row.ativo?"Inativar":"Ativar"}'><i class='fas fa-power-off'></i></button>
                                    <a href='${window.$baseURL}auth/usuarios/${data}' class='btn btn-warning' data-toggle='tooltip' title='Editar'><i class='far fa-edit'></i></a>
                                </span>`;
                    }}
                ];
                options.initComplete = () =>{
                    this.getDefaultOptions().initComplete();
                    this.criarTogglersSituacao();
                };
                return options;
            }
...

If you notice the last column render creates a <a href='${window.$baseURL}auth/usuarios/${data}' ... that obviously isn't a router-link and doesn't trigger vue router properly, causing an undesired page refresh.

I need the link to do a router push instead of a page refresh. How is this possible?

Upvotes: 1

Views: 1147

Answers (1)

ssc-hrep3
ssc-hrep3

Reputation: 16089

There is no good answer to that problem. datatables is not really compatible with Vue.js. With Vue, the usual way to go would be to pass your reactive HTML structure within a slot to such a library. Because datatables requires you to use a render function and return static HTML as a string, you cannot pass any JavaScript logic along.

The main problem is that you need to pass an event from the link tag to the Vue component. One approach would be to pass HTML in the render function which then can be selected with a specific selector (e.g. adding a class). You also need to add the link data/the item's ID to the HTML element (e.g. with a data-link="" attribute). When datatables has finished rendering, you can add a click listener to all the links. This click listener handler function needs to read the link/ID of the link and pass it to the router. Then, you can use Vue's router.push() function.

When you are implementing a solution with the above approach, make sure to assign and remove the click listeners depending on the lifecycle events of datatables. It might be necessary to add and remove the listeners on each page switch.

Upvotes: 1

Related Questions