Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loading JSZip and PDFMake scripts dynamically #43

Closed
smartcorestudio opened this issue Oct 16, 2015 · 8 comments
Closed

Loading JSZip and PDFMake scripts dynamically #43

smartcorestudio opened this issue Oct 16, 2015 · 8 comments

Comments

@smartcorestudio
Copy link

@DataTables, as all we know and as you say "the disadvantage is that these files can significantly increase the amount of Javascript required on your page". It's not a good idea to load extra 500 kB library if it's not used everyday. And it's the only reason that stops me (and many other developers I think) from using it. Is it possible to load these libraries dynamically, only when user clicks "Export to PDF"/"Export to Excel" button for example? I'm not asking for solutions (code examples) here. I'm asking just about a possibility of that. And what do you think about this idea in general?

@DataTables
Copy link
Collaborator

Hi,

Yes that is absolutely possible. The way the scripts operate at the moment doesn't allow for that, but I don't see any reason why, either new buttons could be created that will perform that action, or the existing ones modified.

The only one thing that makes me hesitant about this is that creating and download a file can sometimes (possibly older browsers - I can't remember off the top of my head) only be triggered from a user interaction. If an async delay is introduced while waiting for a library to be loaded, then the result would not longer be due to user interaction (at least as the browser sees it).

I'd also be very reluctant to add a dependancy (for example RequireJS which could be used to resolve this) to Buttons or to hard code paths into it (even if they could be modified as configuration options).

The other point to consider is that the client-side file creation inherently has limitations. Performance for large pdfs is really poor for example and old legacy browsers can't create files beyond a certain size (current browsers as well, but the limits are huge). So server-side creation of these files might increasingly be attractive to address the client library size + the concerns above.

@smartcorestudio
Copy link
Author

Thanks for so fast and detailed answer Allan! If you say that it's technically possible, I think I'll try to implement it (maybe using jQuery.getScript() or jQuery $.ajax({dataType: "script" ...})) in my free time.
I agree with you that in the most cases server-side creation is the best option. But when creating files is not a core feature, it's used just ocasionally and the tables are rather simple client-side aproach with "on-demand" library loading can be acceptable I think. Now I already have "Export to CSV" button in my app, and I'm considering adding some extra file format options for users convenience, but not at the cost of page load time.
Maybe the possibility of dynamic loading of these libs is worth mentioning in the official docs, what do you think?

@DataTables
Copy link
Collaborator

Yes. It might be that it is a good option to provide the ability to load those libraries dynamically in the built in buttons if it can be done with only a small amount of code and in an understandable way (i.e. won't confuse the average developer).

Another option is to provide these buttons as plug-ins. Certainly interested to see what you cook up!

@smartcorestudio
Copy link
Author

I tried to implement it. My general conception is the following

$.fn.dataTable.ext.buttons.exportXLSX = {
    text: 'Export to Excel',
    action: function ( e, dt, node, config ) {
        $.ajax({ 
          url: '/js/datatables/jszip.min.js',
          dataType: "script",
          cache: true,
          success: function(){
              dt.buttons.exportExcel();
          }
        });
    }
};

But unfortunately I haven't found any public functions for exporting data. exportExcel function in the example is fictious. I managed to make it work for now only by dynamic creating hidden "excelHtml5" button and triggering it. Of course it was made only for testing purposes. But if export buttons actions were public methods this scheme could work.
Moreover, I can imagine that one can create custom buttons which (for example) show custom export settings dialog (or doing something else) before running actual export:

  1. User clicks custom button
  2. Something happens before export (dynamic library loading, showing dialog windows etc)
  3. Export method triggers

Is it possible to make export methods public without major code rewrite? Or maybe it's not a good idea actually?

@DataTables
Copy link
Collaborator

You are correct - there isn't a public function for this as I hadn't expected anything other than my built in buttons to require that code. At the moment, I think the only way to make this work would probably be to modify the library code.

I'm trying to think of a way that you could access the functionality that Buttons has internally - it might be possible to extend the the excelHtml5 button type, use your own action function which will load the required scripts and then execute the original action method of that button type. Its a little messy, but I think that is going to be the only way to do it externally.

@michaeldjeffrey
Copy link

I tried my hand at this problem with webpack. I'll be very interested to see if we get any browsers throwing file download errors thinking it's not being caused by the user.

// required to show button.
window.pdfMake = true;

var originalPdfHtml5Action = $.fn.dataTableExt.buttons.pdfHtml5.action;

$.fn.dataTableExt.buttons.pdfHtml5.action = function pdfHtml5Action(e, dt, button, config){
    require.ensure(['pdfmake', 'vfs_fonts'], function _pdfHtml5Action(){
        require('pdfmake');
        require('vfs_fonts');
        originalPdfHtml5Action(e, dt, button, config);
    });
};

I felt okayish doing window.pdfMake = true; because that's not the only check that determines if the button is showable/usable.

require.ensure() is the webpack way of splitting a build, and telling it what to wait for.

I did the same thing with JSZip for excel exporting. The only difference in the two cases is a module system thing, but I'll mention it anyways. pdfMake straps itself to the window always. JSZip, if being used by a module loader, exports itself and you need to put it on the window for DataTables to find it.

// pdfMake
require('pdfMake);  //auto strapped to the window.

// JSZip
window.JAZip = require('jszip');

@DataTables
Copy link
Collaborator

Really - nice. Thanks for sharing this with us!

@smartcorestudio
Copy link
Author

Thanks for sharing it @michaeldjeffrey! Allan's idea and your implementation of it with work well without webpack:

window.JSZip = true;
var originalexcelHtml5Action = $.fn.dataTableExt.buttons.excelHtml5.action;
$.fn.dataTableExt.buttons.excelHtml5.action = function excelHtml5Action(e, dt, button, config){
    $.ajax({
          url: '/js/datatables/jszip.min.js',
          dataType: "script",
          cache: true,
          success: function(){
              originalexcelHtml5Action(e, dt, button, config);
          }
        });
};

So maybe I can close this issue now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants