Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 6 + Webpack, Datatables, jQuery

I have a "Select All" button that uses this code:

<script type='text/javascript'>
  $('#check_all').on("click", function() {
    $('input[type="checkbox"]').click();
  });
</script>

Since I upgraded to Rails 6 + Webpacker, it stopped working. The console shows this error:

Uncaught ReferenceError: $ is not defined

I managed to fix it by altering environment.js from:

environment.plugins.append('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    Popper: ['popper.js', 'default']
  })
)

To:

environment.plugins.append('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    Popper: ['popper.js', 'default']
  })
)

But once that's fixed, datatables breaks.

Any ideas how to have them both working together? Thanks!

like image 390
Maayan Naveh Avatar asked Jan 26 '23 20:01

Maayan Naveh


2 Answers

Moving your own Javascript into the pack file was a good choice for maintainability. However, I'm guessing those are dynamically generated checkboxes. They may not exist when the bind runs. You can rise above the problem by binding to a parent element and using event delegation. document.body is a common binding choice for delegated events with Turbolinks.

I'm not a fan of calling click() on a checkbox. The result isn't always consistent for users, and it fires off a bunch of unnecessary events in the browser. Inverting the checked prop is cleaner and more consistent.

Getting DataTables to load as a module requires an import shim. It's an older package that prefers AMD over CommonJS and trips over itself in the Webpack environment. Fortunately, there's a standard fix for older code, using the imports-loader. Unfortunately, even then it's got a slightly strange factory export, into which you're expected to inject the window and jQuery variables. Fortunately it's also documented here.

Putting that all together, here's a suggested application.js:

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

import $ from 'jquery'

// Add DataTables jQuery plugin
require('imports-loader?define=>false!datatables.net')(window, $)
require('imports-loader?define=>false!datatables.net-select')(window, $)

// Load datatables styles
import 'datatables.net-dt/css/jquery.dataTables.css'
import 'datatables.net-select-dt/css/select.dataTables.css'

$(document).on('turbolinks:load', () => {
  $(document.body).on('click', '#check_all', () => {
    var checkBoxes = $('input[type="checkbox"]')
    checkBoxes.prop("checked", !checkBoxes.prop("checked"))
  })

  // placeholder example for datatable with checkboxes
  $('#example').DataTable({
    columnDefs: [{
      render: (data,type,row) => `<input type="checkbox" value="${row[0]}">`,
      orderable: false,
      targets: 0
    }],
    order: [[ 1, 'asc' ]]
  })
})

You'll need to yarn add imports-loader, since it's a Webpack option.

The ProvidePlugin is not required for any of this. Unless you're depending on it for injection of imports to third-party code, it can be safely removed.

like image 95
inopinatus Avatar answered Jan 28 '23 22:01

inopinatus


run yarn add imports-loader

I use bootstrap4 datatable if you use bootstrap3 or other CSS frameworks, please follow the installation from this link: https://datatables.net/download/

in my config/webpack/loaders/datatable.js

module.exports = {
  test: /datatables\.net.*/,
  use: [{
    loader: 'imports-loader?define=>false'
  }]
}

in my config/webpack/environment.js

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
const coffee =  require('./loaders/coffee')
const datatable =  require('./loaders/datatable')

environment.plugins.append('Provide', new webpack.ProvidePlugin({
  $: 'jquery',
  jQuery: 'jquery',
  jquery: 'jquery',
  'window.jQuery': 'jquery',
  Popper: ['popper.js', 'default']
}))

/**
 * To use jQuery in views
 */
environment.loaders.append('expose', {
  test: require.resolve('jquery'),
  use: [{
    loader: 'expose-loader',
    options: '$'
  }]
})

environment.loaders.prepend('coffee', coffee)
environment.loaders.prepend('coffee', datatable)

module.exports = environment

in my app/javascript/packs/dashboard.js

require( 'jszip' );

require("datatables.net-bs4")(window, $);
require("datatables.net-responsive-bs4")(window, $);
require("datatables.net-buttons-bs4")(window, $);
require("datatables.net-select-bs4")(window, $);

require("datatables.net-bs4/css/dataTables.bootstrap4.css");
require("datatables.net-responsive-bs4/css/responsive.bootstrap4.css");
require("datatables.net-buttons-bs4/css/buttons.bootstrap4.min.css");
require("datatables.net-select-bs4/css/select.bootstrap4.css");

Feel free to user import './datatable' at any .js file in app/javascript/packs you like

like image 25
Kiry Meas Avatar answered Jan 28 '23 22:01

Kiry Meas