DataTables Buttons not showing in Semantic UI using Node (npm)

DataTables Buttons not showing in Semantic UI using Node (npm)

daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

I'm using DataTables in a Vue.js application using semantic-ui for styling. I have installed the datatables.net-buttons and the datatables.net-buttons-se packages and required them in my app.

When using direct insertion method (both with new constructor and adding buttons option in DataTables initialization), the buttons are not generated. The most happens is the dt-buttons div layer is generated when using the new Buttons constructor method.

I have added pdfmake to my Vue.js-Node application but still no results.

What am I missing?

This question has accepted answers - jump to:

Answers

  • allanallan Posts: 63,468Questions: 1Answers: 10,466 Site admin

    Not sure. if you link to a test case showing the issue I might be able to offer some help.

    Allan

  • daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

    Before I set up a test case. See how I have set up my node application.

    The dependencies in my package.json file is as shown below,

    "dependencies": {
        "axios": "^0.16.2",
        "chart.js": "^2.6.0",
        "datatables.net": "^1.10.15",
        "datatables.net-buttons": "^1.4.2",
        "datatables.net-buttons-se": "^1.4.2",
        "datatables.net-se": "^1.10.15",
        "jquery": "^3.2.1",
        "moment": "^2.18.1",
        "numeral": "^2.0.6",
        "pdfmake": "^0.1.33",
        "semantic-ui": "^2.2.11",
        "semantic-ui-calendar": "0.0.8",
        "vue": "^2.3.3",
        "vue-router": "^2.6.0"
      },
    

    I've set up jQuery to be globally accessible using webpack.base.conf.js as so,

      plugins: [
        new webpack.ProvidePlugin({
          // jquery
          $: 'jquery',
          jQuery: 'jquery',
          'window.jQuery': 'jquery',
        })
      ]
    

    jQuery works. I use it accessing the DOM and so on.

    I then require datatables.net and its dependencies in the main App.js

    require('datatables.net')
    require('datatables.net-se')
    require('datatables.net-buttons-se')
    require('datatables.net-buttons')
    

    Now to the part where the action takes place. The DataTable is initialized and set up with direct insertion as follows,

        var tblWork = $('#tbl-work-usage').DataTable({
          processing: true,
          responsive: true,
          filter: false,
          serverSide: true,
          autoWidth: false,
          destroy: true,
          stateSave: true,
          buttons: [ 'pdf', 'excel' ],
          language: {
            emptyTable: 'No records',
            info: '_START_ to _END_ of _TOTAL_',
            infoEmpty: '',
            lengthMenu: '_MENU_',
            paginate: {
              previous: '<i class="left chevron icon"></i>',
              next: '<i class="right chevron icon"></i>'
            },
            aria: {
              paginate: {
                previous: 'Previous',
                next: 'Next'
              }
            }
          },
          columns: [
            {
              data: null,
              title: 'Person',
              width: '25%',
              render: function (data, type, full, meta) {
                return `<a name="person-profile-name" href="#">${data.Person.FirstName} ${data.Person.LastName}</a>`
              }
            },
            {
              data: null,
              title: 'Period',
              width: '13%',
              className: 'right aligned',
              orderable: false,
              render: function (data, type, full, meta) {
                if (moment(data.End).isAfter(data.Begin, 'day')) {
                  return moment(data.Begin).format('MMM D') + ' - ' + moment(data.End).format('D, YYYY')
                } else {
                  return moment(data.End).format('MMM D, YYYY')
                }
              }
            },
            {
              data: null,
              title: 'Logged In',
              width: '10%',
              className: 'right aligned',
              orderable: false,
              render: function (data, type, full, meta) {
                return (data.TotalLoggedInTime ? _.floor(_.divide(data.TotalLoggedInTime, 3600)) : '0') + moment().startOf('day').seconds(data.TotalLoggedInTime).format(':mm:ss')
              }
            },
            {
              data: null,
              title: 'Started',
              width: '10%',
              className: 'right aligned',
              orderable: false,
              render: function (data, type, full, meta) {
                return moment(data.Started, 'H:mm:ss').format('h:mm A')
              }
            },
            {
              data: null,
              title: 'Finished',
              width: '10%',
              className: 'right aligned',
              orderable: false,
              render: function (data, type, full, meta) {
                return moment(data.Finished, 'H:mm:ss').format('h:mm A')
              }
            },
            {
              data: null,
              title: 'Duration',
              width: '10%',
              className: 'right aligned',
              orderable: false,
              render: function (data, type, full, meta) {
                return moment().startOf('day').seconds(data.ShiftDuration).format('H:mm:ss')
              }
            },
            {
              data: null,
              title: 'Break Time',
              width: '10%',
              className: 'right aligned',
              orderable: false,
              render: function (data, type, full, meta) {
                return moment().startOf('day').seconds(_.subtract(data.ShiftDuration, data.TotalLoggedInTime)).format('H:mm:ss')
              }
            },
            {
              data: null,
              title: 'Work Usage',
              width: '12%',
              className: 'right aligned',
              orderable: false,
              render: function (data, type, full, meta) {
                return numeral(data.PercentageLogin).format('0%')
              }
            }
          ],
          order: [[0, 'asc']],
          lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, 'All']],
          ajax: function (data, callback, settings) {
            data.person = $('#filter-person').val() ? $('#filter-person').val() : '00000000-0000-0000-0000-000000000000'
            data.skill = $('#filter-skill').val()
            data.title = $('#filter-title').val()
            data.department = $('#filter-department').val()
            data.showSupervisors = $('#cbx-show-supervisors').is(':checked')
            data.begin = moment($('#filter-begin-date').val(), 'MMMM D, YYYY').format('YYYY-MM-DD')
            data.end = moment($('#filter-end-date').val(), 'MMMM D, YYYY').format('YYYY-MM-DD')
            data.sortColumn = data.order[0].column
            data.sortOrder = data.order[0].dir
    
            delete data['order']
            delete data['columns']
            delete data['search']
            axios({
              method: 'get',
              url: app.data().punchaUrl + 'getworkusagestats',
              params: data
            })
            .then(function (response) {
              callback(response.data.Data)
              if (!response.data.Success) {
                self.pushMessage(response.data.Message)
              }
            })
            .catch(function (error) {
              self.pushMessage(error)
            })
          },
          createdRow: function (row, data, dataIndex) {
            $('a[name=person-profile-name]', row).click(function (e) {
              e.preventDefault()
            })
            $('a[name=person-profile-name]', row).popup({
              position: 'top center',
              inline: true,
              html: self.getProfileDescription(data.Person),
              on: 'click'
            })
          },
          rowCallback: function (row, data) {},
          drawCallback: function (settings) {},
          initComplete: function (settings, json) {}
        })
        tblWork.buttons().container().appendTo($('div.right.aligned.eight.column:eq(0)', tblWork.table().container()))
    

    In the meantime, if I still don't resolve this today, I will set up an equivalent on JSFiddle.

  • daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

    I'm guessing that I'm missing a dependency. What do you think?

  • allanallan Posts: 63,468Questions: 1Answers: 10,466 Site admin

    Looks like you need to include the Buttons files for export, print, etc. The download builder should help there - click the npm tag at the bottom after selecting what you need and it will show the required imports.

    Allan

  • daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

    Ok. I'm seeing some progress after following the documentation that you directed me too.
    I require the following in my App.vue

    require('jszip')
    require('datatables.net')
    require('datatables.net-se')
    require('datatables.net-buttons-se')
    require('../node_modules/datatables.net-buttons/js/buttons.html5.js')
    require('../node_modules/datatables.net-buttons/js/buttons.print.js')
    

    pdfmake was giving me a lot of problems saying that 9 or more dependencies was missing. So I removed it.
    I'm going to need the excel. So, here is my package.json now,

      "dependencies": {
        "axios": "^0.16.2",
        "chart.js": "^2.6.0",
        "datatables.net": "^1.10.15",
        "datatables.net-buttons-se": "^1.4.2",
        "datatables.net-se": "^1.10.15",
        "jquery": "^3.2.1",
        "jszip": "^3.1.4",
        "moment": "^2.18.1",
        "numeral": "^2.0.6",
        "semantic-ui": "^2.2.11",
        "semantic-ui-calendar": "0.0.8",
        "vue": "^2.3.3",
        "vue-router": "^2.6.0"
      },
    

    After, using direct insertion with new keyword to initialize the Buttons extension, like so,

        // [presume that tblOccupancy is initialized]
        const btnsOccupancy = new $.fn.dataTable.Buttons(tblOccupancy, {
          buttons: [ 'copy', 'print', 'excel' ]
        })
        btnsOccupancy.container().appendTo($('div.right.aligned.eight.column:eq(0)', tblOccupancy.table().container()))
    

    After doing all the above, I get [Copy] and [Print]. I'm almost happy.

    I really need the jszip (SheetJS) to work. It's not showing. I'll send you the screenshot following this.

  • daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

    Here is the screenshot of the progress made on the datatable.

  • allanallan Posts: 63,468Questions: 1Answers: 10,466 Site admin

    The Excel export doesn't use SheetJS, so if you need SheetJS you'd probably need to write that export button (I'm not aware of one that exists).

    My guess as to why the Excel button isn't appearing with the above is that Buttons isn't seeing JSZip for some reason. I'm not sure immediately why that would be. I assume you are using AMD loaders (rather than CommonJS)?

    Can you link to the page so I can debug the code directly please.

    Allan

  • daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

    So Allan,

    I do not have that option to expose the application over the internet right now. It's going to take me some time to set up a test environment for you.

    This morning I decided to add some more requirements like the following,

    require('datatables.net-buttons/js/buttons.flash.js')
    

    And voila, the [Excel] button shows up.

    Here's the thing, though, I click the button and it does not work. LOL.

    I'm thinking JSZip is not being seen. Maybe after npm install --save jszip, I need to build with gulp or grunt. Gonna check that out.

    Your help has been useful so far. I know you would need to debug the app. I'm going to try and see how I can put this over the internet if I do not find the solution today.

  • allanallan Posts: 63,468Questions: 1Answers: 10,466 Site admin
    Answer ✓

    Try this:

    window.JSZip = require('jszip');
    

    I've just tried that in another thread and it appears to work okay.

    Allan

  • daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

    It works. Not elegant for Node-Vue.js, but it works.

    Can you provide a more elegant solution that fits into Node-Vue.js apps?
    (If not, that is OK. You did well)

  • allanallan Posts: 63,468Questions: 1Answers: 10,466 Site admin
    Answer ✓

    In what way are you looking for it to be more elegant? I'm not sure what you are looking for - but then I haven't used Vue myself.

    Allan

  • daytonoutardaytonoutar Posts: 25Questions: 7Answers: 1

    I tried,

    const JSZip = require('jszip')
    

    But that throws an error.
    I'll satisfy with,

    window.JSZip = require('jszip')
    
  • allanallan Posts: 63,468Questions: 1Answers: 10,466 Site admin
    edited October 2017 Answer ✓

    The problem with const JSZip is that it would still be locally scoped. Using window.JSZip effectively assigns it to the global scope so that the Buttons extension can see it.

    The CommonJS loader for Buttons has the option of passing in a JSZip instance, but the AMD loader needs it to be in the global scope.

    Allan

This discussion has been closed.