Как экспортировать все строки из Datatables с помощью Ajax?
Я использую новую функцию в Datatables: "кнопки экспорта HTML5". Я загружаю данные с помощью Ajax.
https://datatables.net/extensions/buttons/examples/html5/simple.html
Проблема заключается в том, что он экспортирует только отображаемую страницу.
Я экспортирую вот так:
buttons: [
{
extend: 'pdfHtml5',
text: 'PDF',
exportOptions: {
"columns": ':visible',
}
},
]
Как я могу экспортировать все строки?
Ответы
Ответ 1
В соответствии с Документация DataTables нет способа экспортировать все строки, когда вы используете серверную сторону:
Особое примечание при обработке на стороне сервера: при использовании DataTables в режиме обработки на стороне сервера (serverSide
) selector-modifier
оказывает очень малое влияние на выбранные строки, поскольку вся обработка (заказы, поиск и т.д.) выполняется на сервер. Поэтому единственными строками, которые существуют на стороне клиента, являются те, которые показаны в таблице в любой момент времени, и селектор может выбирать только те строки, которые находятся на текущей странице.
Я работал над этим, добавив параметр "ВСЕ" в меню длины и обучая конечных пользователей отображать все записи перед экспортом 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: '<span class="fa fa-file-excel-o"></span> Excel Export',
exportOptions: {
modifier: {
search: 'applied',
order: 'applied'
}
}
}
],
// other options
});
Ответ 2
Вам нужно сообщить функции AJAX, чтобы получить все данные, затем выполнить экспорт, но отменить фактическую ничью, чтобы все эти данные не загружались в DOM. Полные данные все еще будут сохраняться в памяти для API DataTables, поэтому вам нужно обновить его до того, как оно было до экспорта.
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
},
Ответ 3
Да, это вполне возможно сделать.
Внутри DataTables имеет функцию, называемую button.exportData(). Когда вы нажимаете кнопку, эта функция вызывается и возвращает текущее содержимое страницы.
Вы можете перезаписать эту функцию, чтобы она извлекала все результаты на стороне сервера на основе текущих фильтров. И называя тот же url используемый для ajax pagination.
Вы перезаписываете его перед инициализацией своей таблицы. Код выглядит следующим образом:
$(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()}
}
}
});
Ответ 4
Это определение кнопки работало для меня в прокрученной таблице (вместо пейджинга):
{
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();
}
}
Ответ 5
Я знаю, что это старый вопрос, однако для тех, кто борется с этим, здесь мое решение.
Переменные
var downloading = false,
downloadTimestamp = null;
Определение кнопки загрузки:
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:
ajax: {
url: ajaxURL,
type: 'POST',
data: function(data) {
data.timestamp = new Date().getTime(); //add timestamp to data to be sent, it 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':
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 tables WHERE rowNumber BETWEEN firstRow AND lastRow и выводит результат для нормального отображения в DataTable.
if (download == true), тогда сервер выполняет таблицы SELECT FROM FROM и сохраняет все строки, отформатированные как CSV файл (или любой другой формат файла в зависимости от того, что может создать серверная среда) на стороне сервера для последующего поиска GET.
Ниже приведен код ASP JScript, который я использовал на стороне сервера:
var timestamp = Number(Request.QueryString('ts')), //if it 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 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 = []
Ответ 6
Большое спасибо пользователю "kevinpo". Он дал способ, как все записи из jquery datatable можно загружать как Excel, когда обработка на стороне сервера включена.
Основываясь на его ответе, здесь я реализовал полную функциональность экспорта (copy, excel, csv, pdf, print) для обработки на стороне сервера.
внутри $(document).ready()
определите нижеприведенную функцию & вызывайте эту функцию для action
каждой кнопки экспорта, как показано ниже:
/* For Export Buttons available inside jquery-datatable "server side processing" - Start
- due to "server side processing" jquery datatble does not 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
А для кнопок определите, как показано ниже
"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
}
],
Ответ 7
Ответ Selcuk будет работать абсолютно нормально, если мы сможем зафиксировать значение "Все" заранее.
Предположим, что количество строк хранится в переменной 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: '<span class="fa fa-file-excel-o"></span> Excel Export',
exportOptions: {
modifier: {
search: 'applied',
order: 'applied'
}
}
}
],
// other options
});
Ответ 8
Я использую Datatables Version: 1.10.15 и получил @kevenpo ответ на работу. Мне пришлось немного изменить его, чтобы обрабатывать наши параметры на стороне сервера, но это был единственный камень преткновения. Я изменил его строку: data.length = 2147483647;
до data.params[2]= -1;
, потому что мы сохранили наши параметры на стороне сервера в подпараллеле params. Я еще не тестировал его с очень большим набором данных, чтобы узнать, что такое производительность, но это очень умное решение.
Ответ 9
Если вы используете Laravel Framework, вы можете использовать это...
$.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},
],
});
Ответ 10
@diogenesgg ответ хорош!
но я проверил $.fn.DataTable.Api.register
не поддерживает Promise
Итак, я сначала получил данные.
const {data} = await selectDailyConnectStatistics({
page: 1,
limit: 99999999
}))
excelDatas = data.list
$("#table").DataTable().button('.buttons-excel').trigger();
Второй триггер для экспорта в Excel.
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', ...]
}
}
});
Ответ 11
Просто хотел опубликовать фактический ответ для людей, борющихся с этим.
Если вы экспортируете с помощью кнопки 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 });
}
},