Adding a Print button gives an error

Adding a Print button gives an error

silkspinsilkspin Posts: 152Questions: 34Answers: 5

I've tried to add a Print button using the guide... https://datatables.net/extensions/buttons/examples/print/simple.html

I've added the dependencies, but when I click the "Print" button the console shows...

buttons.print.min.js:5 Uncaught 
TypeError: Cannot read properties of undefined (reading 'map')
    at B.action (buttons.print.min.js:5:1163)
    at action (dataTables.buttons.js:748:19)
    at HTMLButtonElement.<anonymous> (dataTables.buttons.js:769:7)
    at HTMLButtonElement.dispatch (jquery-3.5.1.min.js:2:43090)
    at v.handle (jquery-3.5.1.min.js:2:41074)
action  @   buttons.print.min.js:5
action  @   dataTables.buttons.js:748
(anonymous) @   dataTables.buttons.js:769
dispatch    @   jquery-3.5.1.min.js:2
v.handle    @   jquery-3.5.1.min.js:2

This would be too big and complex to create a test case so I wondered if someone could point me in the correct direction based on the console output?

I also use Ajax to pull in the data via a CSV which might be part of the reason.

  $.ajax({
    url: "import.php",
    method: "POST",
    data: formdata,
    dataType: "json",
    contentType: false,
    cache: false,
    processData: false,
    success: function(jsonData) {
      $("#csv_file").val("");
      table = $("#datatable").DataTable({
        data: jsonData,

        buttons: [
          'print',
          {
            extend: 'colvis',
            columns: ':not(.noVis)',
            postfixButtons: ['colvisRestore'],
          },

You'll notice above I added 'print' inside "buttons" because I have other buttons. This was because if I use the syntax from the guide below it is ignored. I've also replaced the existing buttons but it's still the same problem. I have also tried 'copy', 'csv', 'excel', 'pdf', 'print' and still get the same errors.

    layout: {
        topStart: {
            buttons: ['print']
        }
    }

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    I'm not sure which line, in buttons.print.js, the error is occurring on since you are using the minimized version. But looking at the code it looks like the .map() method is used a few times to gather information about the header and footer.

    Are you using complex header or footer?

    Possibly you can load the unminimized version for debugging so we can see which line the failure is on along with the buttons version.

    Possibly you can build a simple test case with your header and footer to see if you can recreate the issue.

    Kevin

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5

    Thanks @kthorngren. I do use complex headers because I use filters. I've swapped the js. Now I get...

    buttons.print.js:147 Uncaught TypeError: Cannot read properties of undefined (reading 'map')
        at B.action (buttons.print.js:147:42)
        at action (dataTables.buttons.js:748:19)
        at HTMLButtonElement.<anonymous> (dataTables.buttons.js:769:7)
        at HTMLButtonElement.dispatch (jquery-3.5.1.min.js:2:43090)
        at v.handle (jquery-3.5.1.min.js:2:41074)
    

    Line 147 relates to the 2nd line here...

            if (config.header) {
                var headerRows = data.headerStructure.map(function (row) {
                    return (
                        '<tr>' +
                        row
                            .map(function (cell) {
                                return cell
                                    ? '<th colspan="' +
                                            cell.colspan +
                                            '" rowspan="' +
                                            cell.rowspan +
                                            '">' +
                                            cell.title +
                                            '</th>'
                                    : '';
                            })
                            .join('') +
                        '</tr>'
                    );
                });
    

    Are complex headers not compatible with 'copy', 'csv', 'excel', 'pdf', 'print' etc?

    If there isn't a quick fix for this I'll have a go at a test case tomorrow. Now that you've pointed me in the right direction that's really helpful!

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975
    Answer ✓

    With Datatables 2 and the new buttons complex headers are now supported. Here is an exmaple. Make sure the overall colulmn count in each header row matches. The error suggests one of the header rows either has too many columns or not enough.

    Kevin

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5

    Thanks @kthorngren. The complex headers have always matched. I'm just trying to add extra functionality to an existing working Datatable. I currently use dataTables.bootstrap4.min.js so it looks like I will probably need to upgrade quite a few files. That will mean I need to do more testing to make sure other things don't break. Is the upgrade to v2 pretty smooth?

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    What version of Datatables and Buttons do you have?

    You mention using the layout option which is new in Datatables 2.0. If using a previous version then you will need to load an earlier version of buttons. Use the legacy download builder for DT version 1.x.

    Is the upgrade to v2 pretty smooth?

    Its intended to be but there are some changes, like deferRender is now enabled by default, that might catch yo off guard if using row().node(). You definitely need to test it with your DT 1.x packages. Go to the DataTables 2! link at teh top of the forum page for all the new and changed features.

    Kevin

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5

    I've not updated anything for maybe a couple of years because everything was working! I download and self host the files. I will set up a new test area and replace the old files. Thanks for the heads up of possible problems. These are things I'll need to check.

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5

    I took your advice @kthorngren and updated to DataTables 2. So far it looks mostly like it's cosmetic problems I've got rather than functional, but I'll spend the coming days testing things.

    The export buttons do now work with the complex headers, but I have run into problems with customising a couple of things. I can make both the colvis options for print work, or ignoring the 2nd line of the complex header with the filters in, but I can't make them work together.

    https://datatables.net/extensions/buttons/examples/print/columns.html

    https://datatables.net/extensions/buttons/examples/html5/complex-header-specific-rows

    My problem seems to be related to how to handle these 2 parts.

            buttons: [
              {
                extend: 'print',
                exportOptions: {
                  columns: ':visible',
                },
              },
    

    and...

                buttons: [
                    { extend: 'copyHtml5', exportOptions: exportOptions },
                    { extend: 'excelHtml5', exportOptions: exportOptions },
                    { extend: 'pdfHtml5', exportOptions: exportOptions }
                ]
    
  • allanallan Posts: 63,689Questions: 1Answers: 10,500 Site admin

    Can you give me a link to your page, or create an example on https://live.datatables.net showing the issue so I can look into it please?

    Are you wondering how to combine those two buttons arrays together, or is it something else? being able to see the page with the full code would be really useful.

    Thanks,
    Allan

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5
    edited June 5

    Hi @allan. I'm just working on a local dev at the moment, but all I wanted to know is how to merge those 2 code examples on your website so that I can hide the complex header 2nd row, whilst still being able to use colvis and hide certain columns before printing / exporting. I've had a go myself but can't get the combination right.

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    Just combine everything into one array, like this:

    uttons: [
       'colvis',
       {
        extend: 'print',
        exportOptions: {
          columns: ':visible',
        },
        { extend: 'copyHtml5', exportOptions: exportOptions },
        { extend: 'excelHtml5', exportOptions: exportOptions },
        { extend: 'pdfHtml5', exportOptions: exportOptions }
      },
    

    Order them the way you like.

    Kevin

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5

    I've just tried that @kthorngren and it does work for PDF export but not for the Print button. That still shows the 2nd row of the complex header I'm trying to hide. I'm guessing there isn't a Html5 equivalent for print? I did try printHtml5 but that showed an unknown type.

  • allanallan Posts: 63,689Questions: 1Answers: 10,500 Site admin

    What is your exportOptions variable defined as?

    If you link to your page it really would help us to resolve this issue.

    I'm guessing there isn't a Html5 equivalent for print? I did try printHtml5 but that showed an unknown type.

    Correct. It used to be that copy, Excel and pdf were all provided by Flash. When the HTML5 APIs made it possible to do it in Javascript alone, the *Html5 buttons were introduced. There was never a Flash button for print, so there was never a need to distinguish it. It has always be Javascript / HTML only.

    Allan

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5

    I've just removed loads of code that isn't needed and left the main bit if that's OK? If not I'll create a test case later today.

    This is where I've added the code from your 2 examples. The goal for me is to be able to hide columns with colvis and hide the 2nd row of the complex header with the Print button. This currently works fine if I use the PDF button...

    $(document).ready(function() {
      
      let exportOptions = {
          customizeData: function (data) {
              // Remove the second header row by popping it off the array
              data.headerStructure.pop();
              console.log(data);
          }
      };
    
      var table;
      var formElem = $("#upload_csv");
      var formdata = new FormData(formElem[0]);
    
      $.ajax({
        url: "import.php",
        method: "POST",
        data: formdata,
        dataType: "json",
        contentType: false,
        cache: false,
        processData: false,
        success: function(jsonData) {
          $("#csv_file").val("");
    
          table = $("#datatable").DataTable({
            data: jsonData,
            autoWidth: false,
            orderCellsTop: true,
    
    
            buttons: [
    
              {
                extend: 'print',
                exportOptions: {
                  columns: ':visible',
                },
              },
              { extend: 'copyHtml5', exportOptions: exportOptions },
              { extend: 'excelHtml5', exportOptions: exportOptions },
              { extend: 'pdfHtml5', exportOptions: exportOptions },
              {
                extend: 'colvis',
                columns: ':not(.noVis)',
                postfixButtons: ['colvisRestore'],
                attr: {
                  'aria-label': 'menu of columns that can be toggled',
                },
              },
    
              
            ],
    
    
            initComplete: function() {
              tableWidth();
              this.api()
              .columns()
              .every(function () {
                  // Get the input element from the second header row
                  let input = this.header(1).querySelector('input');
                  let column = this;
                  
                  // the Event Listener is in another js file so was removed from here
              });
              buildSelect(this.api());
            },
    
          });
    
          showAfterLoad();
          tableWidth();
          table.on('draw', function() {
            tableWidth();
            buildSelect(table); 
          });
          // clear filters on hidden columns
          $('#datatable').on('column-visibility.dt', function(e, settings, column, state) {
            if (!state) {
              table.columns(column).search('').draw();
            }
            table.columns([0, 5, 6, 7])
              .every(function() {
                table.rowGroup().enable().draw();
                var idx = this.index('visible');
                if (this.search() === '' && idx !== null) {
                  $('#datatable thead tr:eq(1) td:eq(' + idx + ') select').val('');
                  $('#datatable thead tr:eq(1) td:eq(' + idx + ') select').css('color', 'inherit').css('background-color', '#fff');
                }
              });
          });
        },
      }); // end ajax
    
    
    });
    
    
    
  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    To remove the second header row ;from the print export add the customize function to the exportOptions of the print button, for example:

            buttons: [
     
              {
                extend: 'print',
                exportOptions: {
                  columns: ':visible',
                  customizeData: function (data) {
                     // Remove the second header row by popping it off the array
                     data.headerStructure.pop();
                     console.log(data);
                 }
               },
              },
              { extend: 'copyHtml5', exportOptions: exportOptions },
              { extend: 'excelHtml5', exportOptions: exportOptions },
              { extend: 'pdfHtml5', exportOptions: exportOptions },
              {
                extend: 'colvis',
                columns: ':not(.noVis)',
                postfixButtons: ['colvisRestore'],
                attr: {
                  'aria-label': 'menu of columns that can be toggled',
                },
              },
     
               
            ],
    

    Kevin

  • silkspinsilkspin Posts: 152Questions: 34Answers: 5

    That's fantastic @kthorngren! It works great. I didn't realise that print wasn't the same as the other export commands until @allan pointed it out (thanks Allan). This code will be really helpful to others trying to get the same behaviour.

Sign In or Register to comment.