datatables with webpack $.fn.dataTable undefined

datatables with webpack $.fn.dataTable undefined

spigandromedaspigandromeda Posts: 4Questions: 2Answers: 0

Hi,

I am trying to migrate my project to Symfony 4. To do so I want to use webpack/encore instead of static assets. I am unsing the datatables plugin and now I am trying to make it work. After requiring the module I cannot use $.fn.dataTables.

TypeError: $.fn.dataTable is undefined

The module is imported via require('datatables.net');
I also tried to use AMD: require('datatables.net')(window, $); this resulted in TypeError: this is undefined (jquery.dataTables.js:132).

Writing the require output to $.fn.dataTable also doesn't work.
dataTable = require('datatables.net'); $.fn.dataTable = dataTable;

Of course it's no longer undefined but it also doensn't work because the module exports DataTable and not $.fn.dataTable. So the resulting error when unsing it is TypeError: $(...).DataTable is not a function

I would appreciate your help to finally make this work like I would use static assets.

This question has an accepted answers - jump to answer

Answers

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Hi @spigandromeda ,

    Take a look at this thread here - hopefully that will get you going.

    Cheers,

    Colin

  • spigandromedaspigandromeda Posts: 4Questions: 2Answers: 0

    Nope. I am afraight that didn't work.
    I am not sure how datatables and webpack internally work. But I guess the package attaches itself to the jquery space (why we can use $.fn.dataTable).
    So when calling the package via require ... does webpack handle $ to the module so it can attach itself to jquery or isn't that even designated?

  • spigandromedaspigandromeda Posts: 4Questions: 2Answers: 0

    Ok I found a solution. Its not pretty but it works:

    var DataTable = require('datatables.net');
    require( 'datatables.net-buttons' );
    require( 'datatables.net-responsive' );
    
    $.fn.dataTable = DataTable;
    $.fn.dataTableSettings = DataTable.settings;
    $.fn.dataTableExt = DataTable.ext;
    DataTable.$ = $;
    
    $.fn.DataTable = function ( opts ) {
        return $(this).dataTable( opts ).api();
    };
    

    Simple: just adding all functions to $.fn manually. I just took the code lines from the actual code in the nodeJS module.

    If somebody knows the proper solution to add such prototypes from inside the module: please let us know :smile:

  • allanallan Posts: 63,523Questions: 1Answers: 10,473 Site admin

    That shouldn't be required... If you are AMD loading, which it appears you are, then DataTables does this:

            define( ['jquery'], function ( $ ) {
                return factory( $, window, document );
            } );
    

    So as long as jquery is resolvable (it is case sensitive) then it should work.

    Can you confirm both if you are use AMD and if jquery is resolvable in your loader?

    Allan

  • dmezieredmeziere Posts: 5Questions: 0Answers: 0

    Thank you a thousand times spigandromeda ! You have provided the only solution that works for me ! I think I tried any other way I could find on the web for several days now.

  • dmezieredmeziere Posts: 5Questions: 0Answers: 0

    Well, partially. If I try to use datatables.net-bt, after it, again, "DataTable is undefined".

  • allanallan Posts: 63,523Questions: 1Answers: 10,473 Site admin

    I don't think there is a datatables.net-bt package. Perhaps you means dataTables.net-bs?

    Allan

  • dmezieredmeziere Posts: 5Questions: 0Answers: 0

    Well, I don't really know. I am a PHP dev, and I hate JS and it's weird behaviour. I am really forced to use it and loose patience very rapidly with it. But I love DataTables, and use it for years now. That thing, Webpack Encore, is a curse for us PHP developpers.

    What I understood, is that Webpack Encore uses yarn in place of npm, so maybe the packages names are different. I also found that on a blank Symfony 4 project, DataTables works fine. But on my project, the only way I found to make it work is by redefining the $.fn.dataTable thing. and if i try to use plugins with it, they craches.

    It is so frustrating to copy-paste obscur things found on the web and not understand why it would work or why it wouldn't. Having globals, not-so-globals, and maybe-globals variables ... For me JS is not a programming anguage. It isn't even build by the ones that specifies it. Each browsers make their own JS engine, respecting or not the specs ... It couldn't and shouldn't work.

  • dmezieredmeziere Posts: 5Questions: 0Answers: 0
    edited October 2018

    It seems that I made it work. But I don't really know how, as I modified so much things in a week.

    I've replaced all "require()" calls with "import" ones, and defined window.jQuery, and now it seems to work.

    import $ from 'jquery';
    window.jQuery = $;
    import DataTable from 'datatables.net';
    import 'datatables.net-bs';
    

    No more need to redeclare $.fn.dataTable. Like we say in french "C'est tombé en marche". It works, but I can't explain why.

    Edit: It looks like "window.jQuery = $;" isn't event required, thanks to Encore's autoProvidejQuery()

  • dmezieredmeziere Posts: 5Questions: 0Answers: 0

    Ignore my previous message, it's all false :/ Can you even delete it, Allan ?

  • andrewredlineandrewredline Posts: 9Questions: 3Answers: 0

    I just about gave up on using datatables via webpack until I came across the following:

    https://gist.github.com/jrunestone/2fbe5d6d5e425b7c046168b6d6e74e95#file-jquery-datatables-webpack

    Works 100% now using the imports-loader.

    A couple of notes:

    Installing from https://github.com/webpack/imports-loader is fine. https://github.com/webpack/imports-loader#disable-amd is not required.

    Change loader: 'imports?define=>false' to loader: 'imports-loader?define=>false'

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    Excellent, glad all working, and thanks for the suggestions, I'm sure that'll help others,

    C

  • sunixsunix Posts: 4Questions: 0Answers: 0

    You can also just disable amd loader:

    var config = Encore.getWebpackConfig();
    
    config.module.rules.unshift({
      parser: {
        amd: false,
      }
    });
    
    module.exports = config;
    
  • jasper502jasper502 Posts: 25Questions: 9Answers: 0

    Can anyone smarter than me look at my similar issue using yarn?

    https://datatables.net/forums/discussion/57642/installation-issues-rails-yarn-webpacker/

  • ttkaminskittkaminski Posts: 1Questions: 0Answers: 0

    I had the same problem as the OP. In my case, it turned out to be a problem with multiple versions of jQuery being referenced as dependencies. When I installed datatables.net, it included jquery 3.4.1. But my main jquery package was still at 3.3.1. So....

    const $ = require('jquery');  // returns v3.3.1
    var dt = require('datatables.net');  // Internally uses v3.4.1 via amd loader
    

    If you attempt to use $.dataTable() in your code, it's going to fail because dataTable wasn't initialised on the older version of jQuery object.

    My solution was to upgrade my jquery package via npm/yarn:

    yarn upgrade jquery

    So it matches the version of jquery used by datatables.net. Then $.dataTable() is defined and works as expected.

  • smo_usersmo_user Posts: 8Questions: 1Answers: 1

    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

    error :
    image
    image

    If someone have any idea of how to solve this issue it would be great!!

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    It would be worthwhile comparing the different between prod and dev, especially the sources that you are including,

    Colin

  • prantlfprantlf Posts: 4Questions: 0Answers: 0
    edited May 2020

    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.

  • allanallan Posts: 63,523Questions: 1Answers: 10,473 Site admin

    In Chrome's inspector $ is an alias to document.querySelectorAll when something else hasn't already used $ (e.g. jQuery).

    So without window.jQuery = window.$ = $ the above isn't building for you? Can you create a minimal repo reproducing that so I can take a look into it please?

    Allan

  • smo_usersmo_user Posts: 8Questions: 1Answers: 1

    Thanks for the answers!

    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...

    I will try to create a minimal repo it seems better this way and much easier for everyone.

  • smo_usersmo_user Posts: 8Questions: 1Answers: 1

    I have just finished the MOCK but it is working on it... :s
    I will have another look on my main project to try again and find where I have an error...

    I will find it...
    "veni vidi vici" has he said... There is still some work for the vici part though.

  • smo_usersmo_user Posts: 8Questions: 1Answers: 1
    Answer ✓

    I have finally found where was the error and has usual 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).

    Thanks everyone for your help!

  • allanallan Posts: 63,523Questions: 1Answers: 10,473 Site admin

    Interesting - thanks for posting back! Great to hear you've got working now.

    Allan

  • chase439chase439 Posts: 2Questions: 0Answers: 0

    I got the same error and was able to resolve it after reading https://datatables.net/forums/discussion/32542/datatables-and-webpack

    The solution was #2 -- Do NOT immediately invoke datatables.net as instructed at https://www.datatables.net/download/npm. In other words, just require the file without invoking or passing arguments.

    So inside my application.js, I have

    // require datatables without invoking or passing arguments to make it work with webpack
    require('datatables.net');
    require('datatables.net-bs4');
    require('datatables.net-responsive');
    require('datatables.net-responsive-bs4');
    
This discussion has been closed.