exportoptions - jquery datatable pdf title
¿Cómo exportar todas las filas de Datatables usando Ajax? (8)
De acuerdo con la documentación de DataTables, no hay manera de exportar todas las filas cuando está usando el lado del servidor:
Nota especial sobre el procesamiento del lado del servidor: cuando se usa DataTables en el modo de procesamiento del lado del servidor (
serverSide
), elselector-modifier
tiene muy poco efecto en las filas seleccionadas, ya que todo el procesamiento (pedido, búsqueda, etc.) se realiza en el servidor. Por lo tanto, las únicas filas que existen en el lado del cliente son las que se muestran en la tabla en un momento dado, y el selector solo puede seleccionar las filas que están en la página actual.
Resolví esto agregando un parámetro ''TODO'' al menú de longitud y entrenando a los usuarios finales para que muestren todos los registros antes de exportar en PDF (o 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
});
Estoy usando una nueva característica en Datatables: "Botones de exportación HTML5". Estoy cargando datos con el Ajax.
https://datatables.net/extensions/buttons/examples/html5/simple.html
El problema es que solo exporta la página que se muestra actualmente.
Estoy exportando así:
buttons: [
{
extend: ''pdfHtml5'',
text: ''PDF'',
exportOptions: {
"columns": '':visible'',
}
},
]
¿Cómo puedo exportar todas las filas?
Debe indicar a la función AJAX para obtener todos los datos, luego hacer la exportación pero cancelar el sorteo real para que todos esos datos no se carguen en el DOM. Sin embargo, los datos completos seguirán existiendo en la memoria de la API de DataTables, por lo que debe actualizarlos a la forma en que estaba antes de la exportación.
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();
};
y:
buttons: [
{
extend: ''excel'',
action: newExportAction
},
Esta definición de botón funcionó para mí en una tabla desplazada (en lugar de paginación):
{
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();
}
}
Obligará a DataTable a solicitar todas las filas para el filtrado actual para una solicitud. Luego llama directamente a la acción deseada del botón Exportar. La variable addOptions
se puede usar para modificar la configuración estándar del botón de exportación.
Sin embargo, podría tener problemas si tiene muchas filas, ya que todas están cargadas en el DOM.
Estoy usando la versión de Datatables: 1.10.15 y conseguí que la respuesta de @kevenpo funcionara. Tuve que modificarlo un poco para manejar los parámetros del lado del servidor, pero ese fue el único obstáculo. Cambié su línea: data.length = 2147483647;
a data.params[2]= -1;
porque almacenamos nuestros parámetros del lado del servidor en una sub-matriz params. Todavía no lo he probado con un conjunto de datos muy grande para ver cuál es el rendimiento, pero esta es una solución muy inteligente.
La respuesta de Selcuk funcionará absolutamente bien si podemos corregir el valor de "Todo" de antemano. Supongamos que el número de filas se almacena en la variable row_count. Entonces
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
});
Sé que esta es una vieja pregunta, sin embargo, para cualquiera que esté luchando con esto, aquí está mi solución.
Variables:
var downloading = false,
downloadTimestamp = null;
Descargar la definición del botón:
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
}
}
}]
Definición de 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
}
}
Función ''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
}
}
Lado del servidor:
si (descarga == falso) , el servidor ejecuta SELECCIONAR columnas DESDE las tablas DONDE la fila NÚMERO ENTRE firstRow AND lastRow y genera el resultado para la visualización normal dentro de DataTable .
Si (descarga == verdadero) , entonces el servidor ejecuta las columnas SELECCIONAR DE las tablas y almacena todas las filas formateadas como archivo CSV (o cualquier otro formato de archivo dependiendo de lo que su entorno de servidor sea capaz de producir) del lado del servidor para su posterior recuperación por solicitud GET.
A continuación se muestra el código ASP JScript que he usado en el servidor:
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 = []
Sí, es totalmente posible hacer que esto funcione. Internamente, DataTables tiene una función llamada buttons.exportData (). Cuando presiona un botón, se llama a esta función y devuelve el contenido de la página actual. Puede sobrescribir esa función para que obtenga todos los resultados del lado del servidor en función de los filtros actuales. Y llamando a la misma url utilizada para la paginación ajax.
Lo sobrescribes antes de inicializar tu tabla. El código es el siguiente:
$(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()}
}
}
});
Sólo quería publicar una respuesta real para las personas que luchan con esto.
Si está exportando con el botón Excel, puede usar la propiedad del botón customizeData
para formatear los datos que se van a sobresalir un momento antes de exportarlos.
Utilicé esto para hacer una llamada api sincrónica a mi servidor para obtener los datos, devolverlos, masajearlos y luego dejar que continúe su camino. Código abajo.
{
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 });
}
},