How to print child rows
How to print child rows
This question has been asked a number of times and I needed it done so I wanted to post my solution.
Things to note:
1) I created this rather quickly for an express purpose to meet my needs:
a) Child rows had their own DataTables, originally
b) Child rows now can have a small amount of html which is not properly scrubbed
2) There is code in here you probably don't need, like my specific classes
3) Excuse the weird formatting
4) I'm not the best JS programmer so bear with me
OK, on to the code:
First, you need to add an index column, so in your initialization use this as your first column:
columns: [
{
/*
* column exists for purpose of printing/exporting
* only required when using child rows
*/
title : 'index'
, visible : false
, render : function( row, type, full, meta ) {
return meta.row;
}
},
Next, you need to add the print button to your DataTable:
buttons : [
{
extend : 'print'
, action : function( e, dt, button, config ) {
dt_print( e, dt, button, config, true )
} /* true here means table has child rows */
}
Leaving out the extra true
allows you to use the code on datatables without child rows or child rows you don't care to print.
Lastly, you need to modify the original print button code from http://github.com/DataTables/Buttons/blob/master/js/buttons.print.js
I took all the required variables from there and replaced the action function of DataTable.ext.buttons.print to the following:
function dt_print( e, dt, button, config, has_child_rows ) {
var data = dt.buttons.exportData( config.exportOptions );
var addRow = function ( d, tag, child_row ) {
var str = '<tr>';
for ( var i = 0, ien = d.length; i < ien; i++ ) {
if (has_child_rows && ! child_row && i === 0) continue;
str += '<' + tag + '>' + d[i] + '</' + tag + '>';
}
return str + '</tr>';
};
var addSubTable = function ( subtable, colspan ) {
var str = '<tr class="innertable-row">'
+ '<td class="innertable-row" colspan="' + colspan + '">';
// If there is no subtable, there must be other child row content, display that
if ( subtable.length == 0 ) {
str += '<div style="text-align: left">'
str += $( dt.row( row_idx ).child() ).children().children().html();
return str + '<div></td></tr>';
}
// Add header row
var headers = subtable.find('tr').first().find('th,td');
str += '<table class="dataTable no-footer">'
+ '<thead><tr>';
for ( var i = 0; i < headers.length; i++ ) {
str += '<th>' + headers.eq(i).text() + '</th>';
}
str += '</tr></thead><tbody>';
// Add body rows
subtable.find('tbody').children('tr').each(function(index, tr) {
var lines = $('td', tr).map(function(index, td) {
return $(td).text();
});
str += addRow( lines, 'td', true );
});
return str + '</tbody></table></td></tr>';
};
// Construct a table for printing
var html = '<table class="' + dt.table().node().className + '">';
if ( config.header ) {
html += '<thead>' + addRow( data.header, 'th' ) + '</thead>';
}
html += '<tbody>';
for ( var i = 0, ien = data.body.length; i < ien; i++ ) {
html += addRow( data.body[i], 'td' );
if ( has_child_rows ) {
var row_idx = data.body[i][0];
if ( dt.row( row_idx ).child() && dt.row( row_idx ).child.isShown() ) {
html += addSubTable( $( dt.row( row_idx ).child() ).find( 'table:visible' ), data.body[0].length );
}
}
}
html += '</tbody>';
if ( config.footer && data.footer ) {
html += '<tfoot>' + addRow( data.footer, 'th' ) +'</tfoot>';
}
// Open a new window for the printable table
var win = window.open();
var title = config.title;
if ( typeof title === 'function' ) {
title = title();
}
if ( title.indexOf( '*' ) !== -1 ) {
title = title.replace( '*', $('title').text() );
}
win.document.close();
// Inject the title and also a copy of the style and link tags from this
// document so the table can retain its base styling. Note that we have
// to use string manipulation as IE won't allow elements to be created
// in the host document and then appended to the new window.
var head = '<title>'+title+'</title>';
$('style, link').each( function() {
head += _styleToAbs( this );
});
try {
win.document.head.innerHTML = head; // Work around for Edge
}
catch (e) {
$( win.document.head ).html( head ); // Old IE
}
// Inject the table and other surrounding information
win.document.body.innerHTML =
'<h1>' + title + '</h1>'
+ '<div>'
+ (typeof config.message === 'function' ?
config.message( dt, button, config ) :
config.message
)
+ '</div>'
+ html;
$( win.document.body ).addClass('dt-print-view');
$('img', win.document.body).each( function ( i, img ) {
img.setAttribute( 'src', _relToAbs( img.getAttribute('src') ));
});
if ( config.customize ) {
config.customize( win );
}
setTimeout( function() {
if ( config.autoPrint ) {
win.print();
win.close();
}
}, 250 );
}
If you know how to do this better or cleaner, I'd love to hear it.
Answers
Helped me greatly your post!
It works perfectly
Thank you
Could you provide the full example, I tried but not worked.
Hi Bignides.
I am not able use your solution, May you help me?
When I click on print button I get a error:
script.js:90 Uncaught ReferenceError: dt_print is not defined
at s.action (script.js:90)
at q (dataTables.buttons.min.js:13)
at HTMLButtonElement.<anonymous> (dataTables.buttons.min.js:14)
at HTMLButtonElement.dispatch (jquery-3.2.1.js:5206)
at HTMLButtonElement.elemData.handle (jquery-3.2.1.js:5014)
Could you provide the full example, I tried but not worked.
Ok, after trying for 20 minutes to work this solution I opted for a much simpler (albeit hacky) way of doing this.
I ended up doing this because my child row data is loaded in via ajax when the user clicks a row and is not present on page load.
This solution does not use the DataTables "Print" Button!
Add this:
Caveat 1: You need to show all rows on the page before launching window.print() as this does not work with pagination (i.e.
"pageLength": -1
)Caveat 2: Obviously this does not use the dataTables library at all. You'll need to check your formatting/print css
This should work with non-ajax data, just take the
window.print()
call out of theajaxStop()
function (i.e. Remove lines 7 and 9 from the code above).If you do this you may want to add a delay of a few milliseconds to allow the Dom to be updated before launching the print window.
I understand this may not be the solution requested, as we aren't actually using dataTables for anything other than showing/hiding child rows, but it sure is simpler.
@bignides This needed a lot of customization but it gave a general idea on how to do it, thank you so much!
Does anyone know how to implement this on the pdf button?
@jimbrew See my reply on your other thread.
Colin