propiedades exportoptions example ejemplo columns bootstrap jquery ajax asp.net-mvc datatables export

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