-
-
Save jrunestone/2fbe5d6d5e425b7c046168b6d6e74e95 to your computer and use it in GitHub Desktop.
Here's how to make jQuery DataTables work with npm and webpack. DT checks for AMD compatibility first | |
which breaks when you're using CommonJS with webpack. | |
Install DT core: npm install datatables.net | |
Install a DT style: npm install datatables.net-bs (bootstrap) | |
Install the imports-loader webpack plugin: https://github.com/webpack/imports-loader#disable-amd | |
Create a loader "exception" just for DT in webpack.config.js: | |
module: { | |
loaders: [ | |
{ | |
test: /datatables\.net.*/, | |
loader: 'imports?define=>false' | |
} | |
] | |
} | |
Then to initialize DT in your app, do this in your main entry point: | |
// you can use import or require | |
import 'datatables.net'; | |
import dt from 'datatables.net-bs'; | |
dt(window, $); | |
Now you can use .DataTable(): | |
$('table[data-table]').DataTable(); // or whatever you want |
Thanks!
It works like this for me with Symfony Webpack Encore bundle.
webpack.config.js:
Encore.addLoader({ test: /datatables\.net.*/, loader: 'imports-loader?define=>false' })
global.js:
require('datatables.net');
require('datatables.net-responsive')(window);
require('datatables.net-buttons')(window);
Thanks alot.
Thanks for that It almost work form me :)
It is working fine when I compile in development mode but not in production mode...
webpack.config.js :
const webpack = require('webpack');
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
let config = {
entry: {
check_process_nomenclature: path.join(
__dirname,
'src',
'js',
'toto.js'
),
user_login: path.join(__dirname, 'src', 'js', 'tata.js')
},
output: {
path: path.join(__dirname, 'web', 'dist'),
filename: '[name].[chunkhash].bundle.js'
},
resolve: {
extensions: ['.js'],
alias: {
style: path.resolve(__dirname, 'src', 'style')
}
},
module: {
rules: [
{ test: /datatables\.net.*/, loader: 'imports-loader?define=>false' },
{
test: /\.js$/i,
exclude: [/(node_modules)/, /(ckfinder)/],
use: ['babel-loader']
},
{
test: /\.(png|webp|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
use: [
{
loader: 'file-loader',
options: {
publicPath: '/web/dist',
name: '[name].[hash].[ext]'
}
}
]
},
{
test: /\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {}
}
]
},
{
test: /\.s[ac]ss$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {}
},
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin(),
new ManifestPlugin(),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
new CleanWebpackPlugin()
],
optimization: {
minimizer: [
new TerserJSPlugin({
test: /\.js$/i,
exclude: /\/node_modules/,
parallel: true,
sourceMap: true
})
]
},
stats: true,
devtool: 'source-map'
};
module.exports = (env, argv) => {
if (argv.mode === 'production') {
config.mode = 'production';
config.optimization.minimize = true;
} else {
config.mode = 'development';
config.optimization.minimize = false;
}
return config;
};
datatables.js :
import dt from 'datatables.net-bs';
import 'datatables.net-bs/css/dataTables.bootstrap.css';
import 'style/_datatables.css';
dt(window, $);
toto.js :
import { productInformation, alertMsg, autoLogout } from './_common';
import './datatables';
import { modalOnHide } from './_modal_onhide';
import 'style/toto.css';
const $table= $('#table');
$table.DataTable({
autoWidth: true,
paging: true,
lengthMenu: [
[25, 50, 100, 250],
[25, 50, 100, 250]
],
ordering: true,
info: true,
stateSave: true,
});
run :
webpack --mode=production
If someone have any idea of how to solve this issue it would be great!!
Did you set the global jQuery? I saw $.fn.dataTable
defined, but $.fn.DataTable
undefined before I did it. I see $
defined in Chrome. Either it is built in or some extension set it, but it does not come from jQuery. As soon as I set the global jQuery properly, DataTables started to work, including the plugins.
import $ from 'jquery'
window.jQuery = window.$ = $
import JSZip from 'jszip'
window.JSZip = JSZip
import 'pdfmake'
import dataTable from 'datatables.net-bs4'
dataTable(window, $)
import buttons from 'datatables.net-buttons-bs4'
buttons(window, $)
import columnVisibility from 'datatables.net-buttons/js/buttons.colVis.js'
columnVisibility(window, $)
import buttonsHtml5 from 'datatables.net-buttons/js/buttons.html5.js'
buttonsHtml5(window, $)
import buttonsPrint from 'datatables.net-buttons/js/buttons.print.js'
buttonsPrint(window, $)
import colReorder from 'datatables.net-colreorder-bs4'
colReorder(window, $)
import fixedColumns from 'datatables.net-fixedcolumns-bs4'
fixedColumns(window, $)
import scroller from 'datatables.net-scroller-bs4'
scroller(window, $)
I do not use webpack
, but esbuild
to bundle the JavaScript output.
Thanks for your answer!
jQuery is automatically provided through the following piece of code:
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
I have tried different variations by playing around with the plugins and optimization options (by following what is written here) without success...
I have also tried to to set globally jQuery as you did but that didn't change a thing. :(
For now I do create the bundle in development
mode but that's not clean and it's annoyed me a little to not understand what's wrong...
Did you execute the functions imported from DataTables and their plugin modules?
I noticed a peculiar behaviour of DataTables JavaScript bundles, when used as ES6 modules. When importing them, they did nothing. No registration in $.fn
or $.fn.dataTables.ext
. I found, that each module exports an object with the default
property, which points to a function expecting window
and $
as parameters. When this function gets called, the module performs its registration, like it does in an AMD or a plain JavaScript project. That is why I called that function after importing each module and my single bundle.
This is how esbuild
bundles the source from my comment above, which is taken from a project of mine:
const jquery = __import(16 /* jquery ***/);**
const jszip = __import(26 /* jszip */);
const pdfmake = __import(69 /* pdfmake */);
const datatables = __import(3 /* datatables.net-bs4 */);
const datatables2 = __import(4 /* datatables.net-buttons-bs4 */);
const buttons_colVis = __import(5 /* datatables.net-buttons/js/buttons.colVis.js */);
const buttons_html5 = __import(6 /* datatables.net-buttons/js/buttons.html5.js */);
const buttons_print = __import(7 /* datatables.net-buttons/js/buttons.print.js */);
const datatables3 = __import(9 /* datatables.net-colreorder-bs4 */);
const datatables4 = __import(11 /* datatables.net-fixedcolumns-bs4 */);
const datatables5 = __import(12 /* datatables.net-scroller-bs4 */);
window.jQuery = window.$ = jquery.default;
window.JSZip = jszip.default;
datatables.default(window, jquery.default);
datatables2.default(window, jquery.default);
buttons_colVis.default(window, jquery.default);
buttons_html5.default(window, jquery.default);
buttons_print.default(window, jquery.default);
datatables3.default(window, jquery.default);
datatables4.default(window, jquery.default);
datatables5.default(window, jquery.default);
The prolog (simplified here) added by esbuild
to the output bundle shows that DataTables UMD wrappers are called with (exports, module)
to let them adapt to a CommonJS environment. Their exports are re-eported depending on their support of ES6:
__sources = [
(exports, module) => {
// ... source of one imported module as-is
},
// the rest or imported modules
]
__modules = {}
function __import (id) {
let module = __modules[id];
if (!module) {
module = modules[id] = { exports: {} };
__sources[id](module.exports, module);
}
return module.exports.__esModule ?
module.exports : { default : module.exports };
}
A simplified DataTables module shows that DataTables do not export __esModule from their UMD wrapper. That is why their exports are put to the property default
in the single exported object. Also, it shows that unlike in AMD and plain JavaScript environments, it does not execute the factory
function in the CommonJS environment. It just exports it. That is why their exported functions have to be called manually before you try to use their functionality.
(function(factory) {
if (typeof define === "function" && define.amd) {
define(["jquery", "datatables.net", "datatables.net-buttons"],
$ => factory($, window, document));
} else if (typeof exports === "object") {
module.exports = (root, $) => factory($, root, root.document);
} else {
factory(jQuery, window, document);
}
})(function($, window2, document2, undefined) {
$.extend($.fn.dataTable.ext.buttons, {
// additional button declarations
});
return DataTable.Buttons;
});
You may want to inspect your webpack
output to understand what is happening there too.
Oh, I've just noticed that you did call dt(window, $);
in your source and you use no Datatables plugins.
You may need to inspect the (unminified) build output or debug it in the browser to see what is happening with window.$
when your initialization gets executed.
Thanks for you help!
I just found where was my error...
It was completely my fault...
but difficult to find because somewhere unexpected (for me at least).
So in fact the "only" error which was causing this was because as I am learning webpack I read the tree-shaking section of the manual and for some reason I copy and past a part of it in my package.json...
"sideEffects": false
Which was obviously a bad idea/error since everything is not in es6 in my modules (such as datatables but not only).
The config syntax of the loader changed.
Here's what works for me:
https://stackoverflow.com/a/74204588/4653886
thanks!