How to print child rows

How to print child rows

bignidesbignides Posts: 5Questions: 2Answers: 0

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

  • josivanljosivanl Posts: 11Questions: 3Answers: 1

    Helped me greatly your post!

    It works perfectly

    Thank you

  • haithamshahaithamsha Posts: 1Questions: 0Answers: 0

    Could you provide the full example, I tried but not worked.

  • MaycoMayco Posts: 2Questions: 0Answers: 0

    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)

  • JSPróJSPró Posts: 4Questions: 2Answers: 0

    Could you provide the full example, I tried but not worked.

  • DAustinDAustin Posts: 16Questions: 0Answers: 1

    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!

    1. Create your own print button (Mine has an ID of "printAll")
    2. Add this:

      $("#printAll").on("click", function () {
      
          //Open all of the child rows
          $("td.details-control").click();
      
          //Wait for all the ajax requests to finish, then launch the print window
          $(document).ajaxStop(function () {
              window.print()
          })
      })
      

    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 the ajaxStop() 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.

  • islamelshobokshyislamelshobokshy Posts: 99Questions: 20Answers: 1

    @bignides This needed a lot of customization but it gave a general idea on how to do it, thank you so much!

  • jimbrewjimbrew Posts: 6Questions: 1Answers: 0

    Does anyone know how to implement this on the pdf button?

  • colincolin Posts: 15,142Questions: 1Answers: 2,586

    @jimbrew See my reply on your other thread.

    Colin

This discussion has been closed.