it-swarm.com.de

Wie exportiere ich alle Zeilen von Datatables mit Ajax?

Ich verwende eine neue Funktion in Datatables: "HTML5-Exportschaltflächen". Ich lade Daten mit Ajax. 

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

Das Problem ist, dass nur die aktuell angezeigte Seite exportiert wird.

Ich exportiere so:

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

Wie kann ich alle Zeilen exportieren?

18
Fox

Gemäß der Dokumentation zu DataTables gibt es keine Möglichkeit, alle Zeilen zu exportieren, wenn Sie die Serverseite verwenden:

Besonderer Hinweis zur serverseitigen Verarbeitung: Bei Verwendung von DataTables im serverseitigen Verarbeitungsmodus (serverSide) hat der selector-modifier nur geringe Auswirkungen auf die ausgewählten Zeilen, da die gesamte Verarbeitung (Bestellung, Suche usw.) auf dem Server ausgeführt wird. Daher sind auf der Clientseite nur die Zeilen vorhanden, die jeweils in der Tabelle angezeigt werden, und der Selektor kann nur die Zeilen auswählen, die sich auf der aktuellen Seite befinden.

Ich habe dieses Problem gelöst, indem ich dem Längenmenü einen 'ALL'-Parameter hinzugefügt und die Endbenutzer darin trainiert habe, alle Datensätze anzuzeigen, bevor sie einen Export nach PDF (oder XLS) durchführen:

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
});
19
Selcuk

Sie müssen die Funktion AJAX anweisen, um alle Daten abzurufen, und dann den Export durchführen, aber die tatsächliche Ziehung abbrechen, damit nicht alle Daten in das DOM geladen werden. Die vollständigen Daten sind jedoch immer noch im Speicher für die DataTables-API vorhanden. Sie müssen sie also so aktualisieren, wie sie vor dem Export waren.

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();
};

und:

    buttons: [
        {
            extend: 'Excel',
            action: newExportAction
        },
18
kevinpo

Diese Schaltflächendefinition funktionierte für mich in einer gescrollten Tabelle (anstelle von Seitenwechsel): 

{
  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();
  }
}

Dadurch wird die DataTable gezwungen, alle Zeilen für die aktuelle Filterung für eine Anforderung anzufordern. Dann ruft es direkt die gewünschte Aktion der Schaltfläche Export auf. Die Variable addOptions kann verwendet werden, um die Standardkonfiguration der Exportschaltfläche zu ändern.

Sie können jedoch auf Probleme stoßen, wenn Sie viele Zeilen haben, da sie alle in das DOM geladen werden.

4
haui

Ich weiß, dass dies eine alte Frage ist, aber für jeden, der damit zu kämpfen hat, hier meine Lösung.

Variablen:

var downloading = false,
    downloadTimestamp = null;

Download-Schaltflächendefinition:

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-Definition:

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
    }
}

Funktion 'preDrawCallback':

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
    }
}

Serverseitig:

if (download == false) , dann führt der Server SELECT-Spalten aus Tabellen aus, in denen rowNumber zwischen firstRow und lastRow ausgegeben wird und Ergebnisse für die normale Anzeige in DataTable ausgibt.

if (download == true) , dann führt der Server SELECT-Spalten FROM-Tabellen aus und speichert alle als CSV-Datei formatierten Zeilen (oder jedes andere Dateiformat, je nachdem, was Ihre Serverumgebung produzieren kann) Server- Seite für späteren Abruf durch GET-Anfrage.

Der folgende ASP - JScript-Code wurde serverseitig verwendet:

    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 = []

Ja, es ist absolut möglich, dies zu erreichen. Intern hat DataTables eine Funktion namens buttons.exportData (). Wenn Sie eine Taste drücken, wird diese Funktion aufgerufen und gibt den aktuellen Seiteninhalt zurück. Sie können diese Funktion überschreiben, sodass alle serverseitigen Ergebnisse basierend auf aktuellen Filtern abgerufen werden. Und die gleiche URL, die für Ajax-Paginierung verwendet wird. 

Sie überschreiben es, bevor Sie Ihre Tabelle initialisieren. Der Code lautet wie folgt:

$(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
diogenesgg

Vielen Dank an den User "kevinpo". Er hat angegeben, wie alle Datensätze von jquery datatable als Excel heruntergeladen werden sollen, wenn serverseitige Verarbeitung eingeschaltet ist. Basierend auf seiner Antwort habe ich hier eine vollständige Exportfunktion implementiert (Kopieren, Excel, CSV, PDF, Drucken) für serverseitige Verarbeitung .

innerhalb von $(document).ready() definieren Sie die folgende Funktion und rufen Sie diese Funktion auf action jeder Export-Schaltfläche wie folgt auf:

/* For Export Buttons available inside jquery-datatable "server side processing" - Start
- due to "server side processing" jquery datatble doesn't support all data to be exported
- below function makes the datatable to export all records when "server side processing" is on */

function newexportaction(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
            if (button[0].className.indexOf('buttons-copy') >= 0) {
                $.fn.dataTable.ext.buttons.copyHtml5.action.call(self, e, dt, button, config);
            } else if (button[0].className.indexOf('buttons-Excel') >= 0) {
                $.fn.dataTable.ext.buttons.excelHtml5.available(dt, config) ?
                    $.fn.dataTable.ext.buttons.excelHtml5.action.call(self, e, dt, button, config) :
                    $.fn.dataTable.ext.buttons.excelFlash.action.call(self, e, dt, button, config);
            } else if (button[0].className.indexOf('buttons-csv') >= 0) {
                $.fn.dataTable.ext.buttons.csvHtml5.available(dt, config) ?
                    $.fn.dataTable.ext.buttons.csvHtml5.action.call(self, e, dt, button, config) :
                    $.fn.dataTable.ext.buttons.csvFlash.action.call(self, e, dt, button, config);
            } else if (button[0].className.indexOf('buttons-pdf') >= 0) {
                $.fn.dataTable.ext.buttons.pdfHtml5.available(dt, config) ?
                    $.fn.dataTable.ext.buttons.pdfHtml5.action.call(self, e, dt, button, config) :
                    $.fn.dataTable.ext.buttons.pdfFlash.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);
            }
            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();
};
//For Export Buttons available inside jquery-datatable "server side processing" - End

Definieren Sie in Export-Butons wie folgt

"buttons": [
                           {
                               "extend": 'copy',
                               "text": '<i class="fa fa-files-o" style="color: green;"></i>',
                               "titleAttr": 'Copy',                               
                               "action": newexportaction
                           },
                           {
                               "extend": 'Excel',
                               "text": '<i class="fa fa-file-Excel-o" style="color: green;"></i>',
                               "titleAttr": 'Excel',                               
                               "action": newexportaction
                           },
                           {
                               "extend": 'csv',
                               "text": '<i class="fa fa-file-text-o" style="color: green;"></i>',
                               "titleAttr": 'CSV',                               
                               "action": newexportaction
                           },
                           {
                               "extend": 'pdf',
                               "text": '<i class="fa fa-file-pdf-o" style="color: green;"></i>',
                               "titleAttr": 'PDF',                               
                               "action": newexportaction
                           },
                           {
                                "extend": 'print',
                                "text": '<i class="fa fa-print" style="color: green;"></i>',
                                "titleAttr": 'Print',                                
                                "action": newexportaction
                           }
],
1
Chandan Kumar

@diogenesgg Antwort ist gut!

aber ich überprüfte $.fn.DataTable.Api.registerPromise nicht unterstützen

Also habe ich zuerst Daten geholt.

    const {data} = await selectDailyConnectStatistics({
      page: 1,
      limit: 99999999
    }))
    excelDatas = data.list

    $("#table").DataTable().button('.buttons-Excel').trigger();

Zweiter Excel-Export auslösen.

  let excelDatas = []
  $.fn.DataTable.Api.register('buttons.exportData()', function(options) {
    if (this.context.length ) {
      return {
        body: _.map(excelDatas, v=> [v.data, ...]), 
        header: ['colum header name', ...]
      }
    }
  });
0
Juneho Nam

Wenn Sie das Laravel Framework verwenden, können Sie dieses verwenden ....

$.fn.DataTable.Api.register( 'buttons.exportData()', function( options ) {
  if(this.context.length) {

    var src_keyword = $('.dataTables_filter input').val();

    var items = [];

    $.ajax({
      url: "server_side_url",
      success: function (result) {

        var k = 1;
        $.each(result.data, function(key, value) {

          var item = [];

          item.Push(k);
          item.Push(value.username);
          item.Push(value.email);
          item.Push(value.created_at);
          item.Push(value.status);

          // filter search with regex
          arr = $.map(item, function (value) {
            var search = new RegExp(src_keyword, "gi");
            if(value.toString().match(search)) return value;
            return null;
          });

          if(!src_keyword || (src_keyword && arr.length)) {
            items.Push(item);
            k++;
          }
        });
      },
      async: false
    });

    return {
      body: items, 
      // skip actions header
      header: $("#user_table thead tr th").map(function() { 
        if(this.innerHTML!='Actions')
          return this.innerHTML; 
      }).get()
    };
  }
});

var user_table = $('#user_table').DataTable({
  dom: 'Bfrtip',
  buttons: [
  'copy', 'csv', 'Excel', 'pdf', 'print'
  ],
  "oSearch": {"bSmart": false},
  processing: true,
  serverSide: true,
  ajax: {
    url: "server_side_url",
    type: 'GET',
    data: function (d) {
      d.status = "";
    }
  },
  columns: [
  {data: 'DT_RowIndex', name: 'DT_RowIndex'},
  {data: 'username', name: 'username'},
  {data: 'email', name: 'email'},
  {data: 'created_at', name: 'created_at'},
  {data: 'status', name: 'status'},
  {data: 'actions', name: 'actions', orderable: false, searchable: false},
  ],
});
0
Jagatheesh

Die Antwort von Selcuk funktioniert absolut einwandfrei, wenn wir vorher den Wert von "All" festlegen können. Angenommen, die Anzahl der Zeilen wird in der Variablen row_count gespeichert. Dann 

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
}); 

Ich benutze Datatables Version: 1.10.15 und habe die Antwort von @ kevenpo erhalten. Ich musste es ein wenig ändern, um mit unseren serverseitigen Parametern umgehen zu können, aber das war der einzige Stolperstein. Ich habe seine Zeile geändert: data.length = 2147483647; in data.params[2]= -1;, da wir unsere serverseitigen Parameter in einem params-Subarray gespeichert haben. Ich habe es noch nicht mit einem sehr großen Datensatz getestet, um zu sehen, was die Leistung ist, aber dies ist eine sehr clevere Lösung. 

0
Robyn Wyrick