Comment exporter toutes les lignes à partir de Datatables en utilisant Ajax?

j'utilise une nouvelle fonctionnalité dans Datatables:"HTML5 export buttons". Je charge des données avec Ajax.

https://datatables.net/extensions/buttons/examples/html5/simple.html

le problème est qu'il n'exporte que la page qui est actuellement affichée.

j'exporte comme ceci:

buttons: [
    {
        extend: 'pdfHtml5',
        text: 'PDF',
        exportOptions: {
            "columns": ':visible',
        }
    },
]

comment exporter toutes les lignes?

16
demandé sur Michael Antonio 2015-09-21 13:13:41

8 réponses

Selon documentation des données il n'y a aucun moyen d'exporter toutes les lignes lorsque vous utilisez le côté serveur:

Note spéciale sur le traitement côté serveur: lors de l'utilisation de données en mode traitement côté serveur (serverSide)selector-modifier a très peu d'effet sur les lignes sélectionnées puisque tout le traitement (commande, Recherche etc) est effectué sur le serveur. Par conséquent, les seules lignes qui existent du côté client sont celles qui apparaissent dans le tableau à un moment donné, et le sélecteur ne peut sélectionner que les lignes qui se trouvent sur la page courante.

j'ai travaillé autour de cela en ajoutant un paramètre 'ALL' au menu length et en formant les utilisateurs finaux pour afficher tous les enregistrements avant de faire une exportation PDF (ou XLS):

var table = $('#example').DataTable({
    serverSide: true,
    ajax: "/your_ajax_url/",
    lengthMenu: [[25, 100, -1], [25, 100, "All"]],
    pageLength: 25,
    buttons: [
        {
            extend: 'excel',
            text: '<span class="fa fa-file-excel-o"></span> Excel Export',
            exportOptions: {
                modifier: {
                    search: 'applied',
                    order: 'applied'
                }
            }
        }
    ],
    // other options
});
18
répondu Selcuk 2015-10-23 11:50:05

vous devez dire à la fonction AJAX d'obtenir toutes les données, puis faire l'exportation mais annuler le tirage réel pour que toutes ces données ne se chargent pas dans le DOM. Les données complètes existeront toujours en mémoire pour L'API DataTables, donc vous devez les rafraîchir comme avant l'exportation.

var oldExportAction = function (self, e, dt, button, config) {
    if (button[0].className.indexOf('buttons-excel') >= 0) {
        if ($.fn.dataTable.ext.buttons.excelHtml5.available(dt, config)) {
            $.fn.dataTable.ext.buttons.excelHtml5.action.call(self, e, dt, button, config);
        }
        else {
            $.fn.dataTable.ext.buttons.excelFlash.action.call(self, e, dt, button, config);
        }
    } else if (button[0].className.indexOf('buttons-print') >= 0) {
        $.fn.dataTable.ext.buttons.print.action(e, dt, button, config);
    }
};

var newExportAction = function (e, dt, button, config) {
    var self = this;
    var oldStart = dt.settings()[0]._iDisplayStart;

    dt.one('preXhr', function (e, s, data) {
        // Just this once, load all data from the server...
        data.start = 0;
        data.length = 2147483647;

        dt.one('preDraw', function (e, settings) {
            // Call the original action function 
            oldExportAction(self, e, dt, button, config);

            dt.one('preXhr', function (e, s, data) {
                // DataTables thinks the first item displayed is index 0, but we're not drawing that.
                // Set the property to what it was before exporting.
                settings._iDisplayStart = oldStart;
                data.start = oldStart;
            });

            // Reload the grid with the original page. Otherwise, API functions like table.cell(this) don't work properly.
            setTimeout(dt.ajax.reload, 0);

            // Prevent rendering of the full data to the DOM
            return false;
        });
    });

    // Requery the server with the new one-time export settings
    dt.ajax.reload();
};

et:

    buttons: [
        {
            extend: 'excel',
            action: newExportAction
        },
13
répondu kevinpo 2017-06-19 15:55:10

cette définition de bouton a fonctionné pour moi dans une table déroulante (au lieu de pager):

{
  text: 'PDF',
  action: function(e, dt, button, config) {
    dt.one('preXhr', function(e, s, data) {
      data.length = -1;
    }).one('draw', function(e, settings, json, xhr) {
      var pdfButtonConfig = $.fn.DataTable.ext.buttons.pdfHtml5;
      var addOptions = { exportOptions: { "columns" : ":visible" }};

      $.extend(true,pdfButtonConfig,addOptions);
      pdfButtonConfig.action(e, dt, button, pdfButtonConfig);
    }).draw();
  }
}

il va forcer le DataTable à demander toutes les lignes pour le filtrage actuel pour une requête. Puis il appelle directement l'action désirée du bouton Exporter. La variable addOptions peut être utilisé pour modifier la configuration standard du bouton exporter.

Vous pourriez avoir des problèmes, si vous avez beaucoup de lignes qu'ils sont tous chargés dans le DOM.

4
répondu haui 2016-06-30 15:26:32

je sais que c'est une vieille question, mais pour quiconque se débat avec cela, Voici ma solution.

Variables:

var downloading = false,
    downloadTimestamp = null;

bouton de téléchargement définition:

buttons: [{
    text: '<span class="glyphicon glyphicon-save-file" aria-hidden="true"></span>',
    titleAttr: 'CSV',
    className: 'downloadCSV',
    action: function(e, dt, node, config) {
        if (downloading === false) { //if download is in progress, do nothing, else
            node.attr('disabled', 'disabled'); //disable download button to prevent multi-click, probably some sort of *busy* indicator is a good idea

            downloading = true; //set downloading status to *true*

            dt.ajax.reload(); //re-run *DataTables* AJAX query with current filter and sort applied
        }
    }
}]

Ajax définition:

ajax: {
    url: ajaxURL,
    type: 'POST',
    data: function(data) {
        data.timestamp = new Date().getTime(); //add timestamp to data to be sent, it's going to be useful when retrieving produced file server-side

        downloadTimestamp = data.timestamp; //save timestamp in local variable for use with GET request when retrieving produced file client-side

        if (downloading === true) { //if download button was clicked
            data.download = true; //tell server to prepare data for download
            downloading = data.draw; //set which *DataTable* draw is actually a request to produce file for download
        }

        return { data: JSON.stringify(data) }; //pass data to server for processing
    }
}

'preDrawCallback la fonction:

preDrawCallback: function(settings) {
    if (settings.iDraw === downloading) { //if returned *DataTable* draw matches file request draw value
        downloading = false; //set downloading flag to false

        $('.downloadCSV').removeAttr('disabled'); //enable download button

        window.location.href = ajaxURL + '?' + $.param({ ts: downloadTimestamp }); //navigate to AJAX URL with timestamp as parameter to trigger file download. Or You can have hidden IFrame and set its *src* attribute to the address above.

        return false; //as it is file request, table should not be re-drawn
    }
}

côté Serveur:

si(téléchargement == false), puis le serveur exécute choisir les colonnes Des tables où rowNumber entre firstRow et latrow et le résultat des sorties pour l'affichage normal à l'intérieur de DataTable.

si(téléchargement == true), puis le serveur exécute SELECT columns FROM tables et stocke toutes les lignes formatées en fichier CSV (ou tout autre format de fichier dépendant de ce que votre environnement serveur est capable de produire) côté serveur pour la récupération ultérieure par la requête GET.

voici le code ASP JScript qui J'ai utilisé côté serveur:

    var timestamp = Number(Request.QueryString('ts')), //if it's a GET request, get timestamp
        tableData = {
            draw: data.draw,
            recordsTotal: 100, //some number static or dynamic
            recordsFiltered: 10, //some number static or dynamic
            data: []
        };
        jsonData = String(Request.Form('data')), //if it's POST request, get data sent by *DataTable* AJAX
        data = jsonData === 'undefined' || jsonData.length === 0 ? null : JSON.parse(jsonData); //do some error checking (optional)

    if(!isNaN(timestamp)) { //check timestamp is valid
        var csvTextKey = 'download-' + timestamp, //this is where timestamp value is used (can be any other unique value)
            csvText = Session(csvTextKey); //obtain saved CSV text from local server-side storage

        if(typeof csvText === 'undefined') { //if CSV text does not exist in local storage, return nothing (or throw error is You wish)
            Response.End();
        }

        //if CSV exists:
        Response.ContentType = 'text/csv'; //set response mime type
        Response.AddHeader('Content-Disposition', 'attachment; filename=test.csv'); //add header to tell browser that content should be downloaded as file and not displayed

        Response.Write(csvText); //send all content to browser

        Response.End(); //stop further server-side code execution
    }

    //if timestamp is not valid then we assume this is POST request, hence data should be either prepared for display or stored for file creation

    if(typeof data !== 'object' || data === null) { //do some more clever error checking
        throw 'data is not an object or is null';
    }

        var recordset = data.download === true ? sqlConnection.Execute('SELECT * FROM #FinalTable') : Utilities.prepAndRunSQLQuery('SELECT * FROM #FinalTable WHERE rowId BETWEEN ? AND ?', [data.start, data.start + data.length], //execute SELECT either for display or for file creation
            headerRow = [],
            sqlHeaderRow = [],
            exportData = [];; 

        if(data.download === true) { //create CSV file (or any other file)
            if(!Array.isArray(data.columns)) {
                throw 'data.columns is not an array';
            }

            for(var i = 0, dataColumnsCount = data.columns.length; i < dataColumnsCount; ++i) {
                var dataColumn = data.columns[i], //get columns data object sent by client
                    title = dataColumn.title, //this is custom property set on client-side (not shown in code above)
                    sqlColumnName = typeof dataColumn.data === 'string' ? dataColumn.data : (typeof dataColumn.data.display === 'string' ? dataColumn.data.display : dataColumn.data['_']); //set SQL table column name variable

                if(typeof title === 'string' && typeof sqlColumnName === 'string' && columnNames.indexOf(sqlColumnName) > -1) { //some more error checking
                    headerRow.push(title);
                    sqlHeaderRow.push(sqlColumnName);
                }
            }

            exportData.push('"' + headerRow.join('","') + '"'); //add table header row to in CSV file format
        }

        while(recordset.EOF === false) { //iterate through recordset
            if(data.download === true) { //if download flag is set build string containing CSV content
                var row = [];

                for(var i = 0, count = sqlHeaderRow.length; i < count; ++i) {
                    row.push(String(recordset.Fields(sqlHeaderRow[i]).Value).replace('"', '""'));
                }

                exportData.push('"' + row.join('","') + '"');
            }

            else { //else format data for display
                var row = {};

                for(var i = 1, fieldsCount = recordset.Fields.Count; i < fieldsCount; ++i) {
                    var field = recordset.Fields(i),
                        name = field.Name,
                        value = field.Value;

                    row[name] = value;
                }

                tableData.data.push(row);
            }

            recordset.MoveNext();
        }

if(data.download === true) { //save CSV content in server-side storage
    Session('download-' + data.timestamp) = exportData.join('\r\n'); //this is where timestamp value is used (can be any other unique value)
}

Response.Write(JSON.stringify(tableData)); //return data for display, if download flag is set, tableData.data = []
3
répondu Mažvydas Tadaravičius 2016-08-03 15:18:42

Oui, il est tout à fait possible de faire en sorte que cela fonctionne. À l'interne, les DataTables ont une fonction appelée buttons.exportData (). Lorsque vous appuyez sur un bouton, cette fonction est appelée et renvoie le contenu de la page. Vous pouvez écraser cette fonction de sorte qu'il tire tous les résultats côté serveur basé sur les filtres actuels. Et appelant la même url utilisée pour la pagination ajax.

Vous l'écrasez avant d'initialiser votre table. Le code est comme suit:

$(document).ready(function() {

    jQuery.fn.DataTable.Api.register( 'buttons.exportData()', function ( options ) {
            if ( this.context.length ) {
                var jsonResult = $.ajax({
                    url: 'myServerSide.json?page=all',
                    data: {search: $(#search).val()},
                    success: function (result) {
                        //Do nothing
                    },
                    async: false
                });

                return {body: jsonResult.responseJSON.data, header: $("#myTable thead tr th").map(function() { return this.innerHTML; }).get()};
            }
        } );

    $("#myTable ").DataTable(
        {
            "dom": 'lBrtip',
            "pageLength": 5, 
            "buttons": ['csv','print', 'excel', 'pdf'],
            "processing": true,
            "serverSide": true,
            "ajax": {
                "url": "myServerSide.json",
                "type": 'GET',
                "data": {search: $(#search).val()} 
            }
        }
});
3
répondu diogenesgg 2017-05-16 02:43:34

la réponse de Selcuk fonctionnera très bien si nous pouvons fixer la valeur de "tout" à l'avance. Supposons que le nombre de lignes soit stocké dans la variable row_count. Puis

var row_count = $("#row_count").val();
var table = $('#example').DataTable({
    serverSide: true,
    ajax: "/your_ajax_url/",
    lengthMenu: [[25, 100, row_count], [25, 100, "All"]],
    pageLength: 25,
    buttons: [
        {
            extend: 'excel',
            text: '<span class="fa fa-file-excel-o"></span> Excel Export',
            exportOptions: {
                modifier: {
                    search: 'applied',
                    order: 'applied'
                }
            }
        }
    ],
    // other options
}); 
0
répondu Mrityunjoy Choudhury 2016-12-27 04:27:56

j'utilise la Version de Datatables: 1.10.15, et j'ai la réponse de @kevenpo pour travailler. J'ai dû modifier un peu pour gérer notre côté serveur les paramètres, mais c'est la seule pierre d'achoppement. J'ai changé sa ligne: data.length = 2147483647;data.params[2]= -1; parce que nous avons stocké nos paramètres côté serveur dans un sous-tableau params. Je ne l'ai pas encore testé avec un très grand ensemble de données pour voir quelles sont les performances, mais c'est une solution très intelligente.

0
répondu Robyn Wyrick 2017-07-12 06:50:57

je voulais juste poster une vraie réponse pour les gens qui se débattent avec ça.

si vous exportez en utilisant le bouton excel, vous pouvez utiliser le customizeData propriété de bouton pour formater les données va exceller un moment avant qu'il exporte.

j'ai utilisé ceci pour faire un appel d'api synchrone à mon serveur pour récupérer les données, les retourner, les masser, et ensuite les laisser continuer sur leur chemin. Le Code ci-dessous.

                           {
                extend: 'excel',
                customizeData: function (p)
                {
                    //get the params for the last datatables ajax call
                    var params = JSON.parse(options.dataTable.ajax.params());
                    //flag to tell the server to ignore paging info and get everything that matches the filter
                    params.export = true;
                    UC.Api(options.api.read.getHook(), params, function (data)
                    {
                        p.body = new Array();
                        $.each(data.data, function (i, d)
                        {
                            var item = [d.serial, UC.FormatDateToLocal(d.meta.Date), d.transmission.title, d.transmission.type, d.transmission.information];
                            p.body.push(item);
                        });
                    }, null, { async: false });
                }
            },
-1
répondu JMD 2018-01-04 16:15:31