如何使用Ajax从Datatables导出所有行?

我正在使用Datatables中的新function:“HTML5导出按钮”。 我正在使用Ajax加载数据。

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

问题是它只导出当前显示的页面。

我这样出口:

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

如何导出所有行?

根据DataTables文档 ,当您使用服务器端时,无法导出所有行:

关于服务器端处理的特别说明:在服务器端处理模式( serverSide )中使用DataTables时, selector-modifier对所选行的影响非常小,因为所有处理(排序,搜索等)都在服务器上执行。 因此,客户端上存在的唯一行是任何时候在表中显示的行,并且选择器只能选择当前页面上的那些行。

我通过在长度菜单中添加“ALL”参数并培训最终用户在执行PDF(或XLS)导出之前显示所有记录来解决这个问题:

 var table = $('#example').DataTable({ serverSide: true, ajax: "/your_ajax_url/", lengthMenu: [[25, 100, -1], [25, 100, "All"]], pageLength: 25, buttons: [ { extend: 'excel', text: ' Excel Export', exportOptions: { modifier: { search: 'applied', order: 'applied' } } } ], // other options }); 

您需要告诉AJAX函数获取所有数据,然后执行导出但取消实际绘制,以便所有数据都不会加载到DOM中。 完整数据仍将存在于DataTables API的内存中,因此您需要将其刷新到导出之前的状态。

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

和:

  buttons: [ { extend: 'excel', action: newExportAction }, 

这个按钮定义适用于滚动表(而不是分页):

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

我知道这是一个古老的问题,但对于任何挣扎于此的人来说,这是我的解决方案。

变量:

 var downloading = false, downloadTimestamp = null; 

下载按钮定义:

 buttons: [{ text: '', 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定义:

 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’function:

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

服务器端:

if(download == false) ,然后服务器执行SELECT列FROM表WHERE rowNumber BETWEEN firstRow和lastRow并输出结果以便在DataTable中正常显示。

if(download == true) ,然后服务器执行SELECT列FROM表并存储格式化为CSV文件的所有行(或任何其他文件格式,具体取决于您的服务器环境能够生成的内容)服务器端,以便以后通过GET请求进行检索。

以下是我在服务器端使用的ASP JScript代码:

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

是的,这完全有可能使这项工作。 在内部,DataTables有一个名为buttons.exportData()的函数。 按下按钮时,将调用此函数并返回当前页面内容。 您可以覆盖该function,以便根据当前filter提取所有服务器端结果。 并调用用于ajax分页的相同URL。

在初始化表之前覆盖它。 代码如下:

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

如果我们事先能确定“全部”的价值,那么塞尔丘克的答案绝对可以。 假设行数存储在变量row_count中。 然后

 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: ' Excel Export', exportOptions: { modifier: { search: 'applied', order: 'applied' } } } ], // other options }); 

我正在使用Datatables版本:1.10.15,并获得了@ kevenpo的工作答案。 我不得不修改它以处理我们的服务器端参数,但这是唯一的绊脚石。 我更改了他的行: data.length = 2147483647; to data.params[2]= -1; 因为我们将服务器端参数存储在params子数组中。 我还没有用一个非常大的数据集测试它,看看性能是什么,但这是一个非常聪明的解决方案。

只是想为那些正在努力解决这个问题的人发布一个实际答案。

如果使用excel按钮导出,则可以使用customizeData按钮属性来格式化导出之前的数据。

我使用它来对我的服务器进行同步api调用以获取数据,返回它,按下它,然后让它继续运行。 代码如下。

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