Uncaught TypeError: Cannot read properties of undefined (reading 'classes')

Uncaught TypeError: Cannot read properties of undefined (reading 'classes')

kevincapkevincap Posts: 5Questions: 1Answers: 0

Link to test case: (unable to provide because it sits behind a secure/authenticated area)
Debugger code (debug.datatables.net): (can't get it to work with webpack because jQuery is not defined for the console to use)

Error messages shown:

Uncaught TypeError: Cannot read properties of undefined (reading 'classes')
    at ./node_modules/datatables.net-bs4/js/dataTables.bootstrap4.mjs (admin.js:7683:93)
    at __webpack_require__ (runtime.js:80:30)
    at Module.<anonymous> (admin.js:12:160)
    at ./assets/js/admin.js (admin.js:66:30)
    at __webpack_require__ (runtime.js:80:30)
    at checkDeferredModules (runtime.js:46:23)
    at Array.webpackJsonpCallback [as push] (runtime.js:33:19)
    at admin.js:1:57

Description of problem:
For a couple years my code has been fine, something in the underlying datatables packages seems to have changed and I've been getting the following error below.
I've tried many of the suggestions in various posts about using bootstrap datatables with encore webpack and I can't get any clarity so I am showing my specifics in hopes someone else can help me.

admin.js, this imports the required datatables packages:

// Require jQuery
let $ = require('jquery');
global.$ = global.jQuery = $;
window.$ = window.jQuery = $;

import dt from 'datatables.net-bs4';
dt(window, $);

import dtb from 'datatables.net-buttons';
dtb(window, $);

import dtb5 from 'datatables.net-buttons/js/buttons.html5.js';
dtb5(window, $);

import dtbp from 'datatables.net-buttons/js/buttons.print.js';
dtbp(window, $);

require('bootstrap');

$(document).ready(function () {
  let enrollmentTable = $('#enrollment_report');
    if (enrollmentTable.length) {
        enrollmentTable.DataTable({
            dom: 'lfrtiBp',
            buttons: [
                'csv'
            ]
        });
    }
});

my webpack.config.js related to imports-loader:

.addLoader({ test: /datatables\.net.*/, loader: 'imports-loader?define=>false' })

my package.json as related to datatables, loaded with yarn. I've tried moving to the latest versions, but that either didn't help or gave me other "undefined" type errors.

        "datatables.net": "^1.10.20",
        "datatables.net-bs4": "^1.10.23",
        "datatables.net-buttons": "^1.6.5",
        "datatables.net-buttons-bs": "^1.6.5",
        "imports-loader": "^0.8.0",

The source line where the error comes from:
at ./node_modules/datatables.net-bs4/js/dataTables.bootstrap4.mjs

/* Default class modification */
jquery__WEBPACK_IMPORTED_MODULE_0__.extend( datatables_net__WEBPACK_IMPORTED_MODULE_1__.ext.classes, {
    sWrapper:      "dataTables_wrapper dt-bootstrap4",
    sFilterInput:  "form-control form-control-sm",
    sLengthSelect: "custom-select custom-select-sm form-control form-control-sm",
    sProcessing:   "dataTables_processing card",
    sPageButton:   "paginate_button page-item"
} );

I suspect the mystery to solve is where is this ".ext.classes" supposed to get defined?

Appreciate any tips on things to try, I've been hitting the wall for several days now and not getting any closer.
Thanks,
Kevin

Answers

  • allanallan Posts: 61,451Questions: 1Answers: 10,055 Site admin

    Hi Kevin,

    I suspect you are running into this issue. Specifically, if you are using ES imports (which I guess is happening in this case) then:

    import dt from 'datatables.net-bs4';
    dt(window, $);
    

    Should become:

    import dt from 'datatables.net-bs4';
    

    And others such as Buttons:

    import 'datatables.net-buttons-bs4';
    

    Let me know how you get on with that.

    Allan

  • kevincapkevincap Posts: 5Questions: 1Answers: 0

    Hi Allan,
    Thank you so much for the response/tips.
    So I did some experimenting this morning and have discovered the following:

    First, upgraded to your latest as so.

            "datatables.net": "^1.13.0",
            "datatables.net-bs4": "^1.13.0",
            "datatables.net-buttons-bs4": "^2.3.3",
    

    Next, I was able to get raw datatables (i.e. no bootstrap4 styling) functioning with just (either way worked the same) using:

    import 'datatables.net';
    

    OR

    import DataTable from 'datatables.net';
    

    The issue is that as soon as I try to do the same type of importing with '-bs4' packages I get my error. My understanding is that I can just import the '-bs4' packages and it will pull in what is needed from the base 'datatables.net' packages, if that's incorrect please let me know. Here is what I tired which always results in the undefined (reading 'classes') error in my original post.

    import 'datatables.net-bs4';
    

    OR

    import DataTable from 'datatables.net-bs4';
    

    Btw, I did try something like this as well, but resulted in the exact same error:

    import 'datatables.net';
    import 'datatables.net-bs4';
    

    Not sure if that's helpful or just muddies up the water.
    -Kevin

  • allanallan Posts: 61,451Questions: 1Answers: 10,055 Site admin

    Could you show me all the DataTables imports you are using? There is actually a packaging issue with some of the current extensions which means you need to include their core library as well as the Bootstrap 4 integration - e.g.:

    import 'datatables.net-buttons';
    import 'datatables.net-buttons-bs4';
    

    Only the second line should be needed, but at the moment with a CommonJS loader both are needed.

    Do you know if WebPack is using CommonJS - I'm guessing it probably is in which case you'd still need the dt(window, $); parts (the CommonJS signature is unchanged)? The ESM signature is different and that what we might initially be running into here.

    Allan

  • kevincapkevincap Posts: 5Questions: 1Answers: 0

    I believe webpack is using CommonJS. Which begs the question why are we hitting the error in .mjs file, I thought it'd be trying to load the .js version?
    Here are all my DataTables imports:

    import DataTable from 'datatables.net';
    DataTable(window, $);
    import dt4 from 'datatables.net-bs4';
    dt4(window, $);
    import dtb from 'datatables.net-buttons';
    dtb(window, $);
    import dtb4 from 'datatables.net-buttons-bs4';
    dtb4(window, $);
    

    Note, if I remove the imports related to bs-4, I will get the following error:

    admin.js:7669 Uncaught TypeError: Cannot read properties of undefined (reading 'buttons')
        at ./node_modules/datatables.net-buttons/js/dataTables.buttons.mjs (admin.js:7669:66)
        at __webpack_require__ (runtime.js:80:30)
        at Module.<anonymous> (admin.js:15:80)
        at ./assets/js/admin.js (admin.js:85:30)
        at __webpack_require__ (runtime.js:80:30)
        at checkDeferredModules (runtime.js:46:23)
        at Array.webpackJsonpCallback [as push] (runtime.js:33:19)
        at admin.js:1:57
    

    which reveals at the source here on line 8:

    // Used for namespacing events added to the document by each instance, so they
    // can be removed on destroy
    var _instCounter = 0;
    
    // Button namespacing counter for namespacing events on individual buttons
    var _buttonCounter = 0;
    
    var _dtButtons = datatables_net__WEBPACK_IMPORTED_MODULE_1__.ext.buttons;
    
    // Allow for jQuery slim
    

    -Kevin

  • xkyleaxkylea Posts: 1Questions: 0Answers: 0

    Hi,
    I have exactly the same problem, have you found a workaround for this bug?

  • allanallan Posts: 61,451Questions: 1Answers: 10,055 Site admin

    Apologies - I lost track of this thread.

    Since ESM is being used, you should use:

    import DataTable from 'datatables.net';
    import dt4 from 'datatables.net-bs4';
    import dtb from 'datatables.net-buttons';
    import dtb4 from 'datatables.net-buttons-bs4';
    

    i.e. no need to execute them as a function.

    If that doesn't help resolve the issue, please create a repo of StackBlitz example showing the issue I can dig into it more.

    Thanks,
    Allan

  • fredsmithfredsmith Posts: 4Questions: 1Answers: 0

    Thank you very much for this advice which solved my similar problem. As a tip for anyone else coming here from google, if you still get the error check if you have imported any js files directly from eg the buttons package such as import 'datatables.net-buttons/js/buttons.print.js' and make sure to point to the mjs files instead.

  • kevincapkevincap Posts: 5Questions: 1Answers: 0

    Hi Allan, thanks for coming back, I too was off on other projects. :)

    So, I removed every DT package and started over from scratch.
    Even the most minimal imports and leaving bootstrap stuff out of it, I still fail:

    import $ from 'jquery';
    
    import DataTable from 'datatables.net';
    import dtb from 'datatables.net-buttons';
    
    const blogTable = $('#blog_index');
    if (blogTable.length) {
        blogTable.DataTable({
            paging: true,
            pageLength: 15,
            dom: 'lfrtip'
        });
    }
    

    package.json:

            "datatables.net": "^1.13.1",
            "datatables.net-buttons": "^2.3.3",
    

    Error:

            vendors-node_modules_dropzone_dist_dropzone_js-node_modules_jquery-cropper_dist_jquery-croppe-6f8bf2.js:34929 Uncaught TypeError: Cannot set properties of undefined (setting 'Buttons')
                at ./node_modules/datatables.net-buttons/js/dataTables.buttons.mjs (vendors-node_modules_dropzone_dist_dropzone_js-node_modules_jquery-cropper_dist_jquery-croppe-6f8bf2.js:34929:58)
                at __webpack_require__ (runtime.js:23:42)
                at ./assets/js/admin/blog.js (admin.js:99:80)
                at __webpack_require__ (runtime.js:23:42)
                at ./assets/js/admin.js (admin.js:56:1)
                at __webpack_require__ (runtime.js:23:42)
                at __webpack_exec__ (admin.js:414:61)
                at admin.js:415:260
                at __webpack_require__.O (runtime.js:67:23)
                at admin.js:416:56
    

    Starting to feel like this is something inside my setup since others seem to be having better luck than I. I'll keep poking around and see what I can uncover. If I get a chance I'll drop something in StackBlitz per your suggestion.

  • allanallan Posts: 61,451Questions: 1Answers: 10,055 Site admin

    If you do console.log(DataTable) - what does the console show please?

    Allan

  • TJVTJV Posts: 2Questions: 0Answers: 0

    In my project, I have the same issue:

    import DataTable from 'datatables.net';
    import 'datatables.net-bs';
    import 'datatables.net-buttons-bs';
    

    results in the following error:

    dataTables.buttons.mjs:2451 Uncaught TypeError: Cannot set properties of undefined (setting 'Buttons')
        at ./node_modules/datatables.net-buttons/js/dataTables.buttons.mjs (dataTables.buttons.mjs:2451:5)
    

    Which is this code

    At that position, console.log($.fn) is undefined, so $.fn.dataTable and$.fn.DataTable are undefined too, of course.

  • allanallan Posts: 61,451Questions: 1Answers: 10,055 Site admin

    Thanks! How are you compiling this? I assume you are using a bundler such as Vite, or Webpack? Can you give me instructions on how to recreate that setup so I can dig into it?

    Thanks,
    Allan

  • TJVTJV Posts: 2Questions: 0Answers: 0

    I'm using Webpack indeed, unfortunately I'm not very experienced with it and it is part of a larger project (in fact using Webpack Encore). I'll see if I can make a minimal reproduction of the problem

  • psaidpsaid Posts: 2Questions: 1Answers: 0

    TJV i'm having the same error. Does anyone already have a solution?!

  • allanallan Posts: 61,451Questions: 1Answers: 10,055 Site admin

    If you can create a mini-repo showing the issue, I can debug it.

    Allan

  • kevincapkevincap Posts: 5Questions: 1Answers: 0

    Hi Allan,
    I was finally able to find the time to fire up a new symfony project from scratch and get datatables working with symfony 6, webpack, encore, bootstrap5.

    So, whatever is going on with my older project is a matter of conflicts somewhere else in all of my dependencies.

    Anyway here are the basics and I hope it can help others.

    webpack.config.js:

    const Encore = require('@symfony/webpack-encore');
    
    // Manually configure the runtime environment if not already configured yet by the "encore" command.
    // It's useful when you use tools that rely on webpack.config.js file.
    if (!Encore.isRuntimeEnvironmentConfigured()) {
        Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
    }
    
    Encore
        // directory where compiled assets will be stored
        .setOutputPath('public/build/')
        // public path used by the web server to access the output path
        .setPublicPath('/build')
        // only needed for CDN's or subdirectory deploy
        //.setManifestKeyPrefix('build/')
    
        /*
         * ENTRY CONFIG
         *
         * Each entry will result in one JavaScript file (e.g. app.js)
         * and one CSS file (e.g. app.css) if your JavaScript imports CSS.
         */
        .addEntry('app', './assets/app.js')
    
        // When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
        .splitEntryChunks()
    
        // will require an extra script tag for runtime.js
        // but, you probably want this, unless you're building a single-page app
        .enableSingleRuntimeChunk()
    
        /*
         * FEATURE CONFIG
         *
         * Enable & configure other features below. For a full
         * list of features, see:
         * https://symfony.com/doc/current/frontend.html#adding-more-features
         */
        .cleanupOutputBeforeBuild()
        .enableBuildNotifications()
        .enableSourceMaps(!Encore.isProduction())
        // enables hashed filenames (e.g. app.abc123.css)
        .enableVersioning(Encore.isProduction())
    
        // enables and configure @babel/preset-env polyfills
        .configureBabelPresetEnv((config) => {
            config.useBuiltIns = 'usage';
            config.corejs = '3.23';
        })
    ;
    module.exports = Encore.getWebpackConfig();
    

    app.js:

    import './styles/app.css';
    
    import $ from 'jquery';
    import 'bootstrap';
    import DataTable from 'datatables.net';
    import 'datatables.net-bs';
    import 'datatables.net-bs5';
    import 'datatables.net-buttons-bs';
    import 'datatables.net-buttons-bs5';
    
    $(document).ready(function() {
        const dt = new DataTable('#example');
    })
    

    styles/app.css:

    @import "bootstrap";
    body {
        background-color: lightgray;
    }
    
    

    package.json:

    {
        "devDependencies": {
            "@babel/core": "^7.17.0",
            "@babel/preset-env": "^7.16.0",
            "@symfony/webpack-encore": "^4.0.0",
            "core-js": "^3.23.0",
            "regenerator-runtime": "^0.13.9",
            "webpack": "^5.74.0",
            "webpack-cli": "^4.10.0",
            "webpack-notifier": "^1.15.0"
        },
        "license": "UNLICENSED",
        "private": true,
        "scripts": {
            "dev-server": "encore dev-server",
            "dev": "encore dev",
            "watch": "encore dev --watch",
            "build": "encore production --progress"
        },
        "dependencies": {
            "@popperjs/core": "^2.11.7",
            "bootstrap": "^5.3.0",
            "datatables.net": "^1.13.5",
            "datatables.net-bs": "^1.13.5",
            "datatables.net-bs5": "^1.13.5",
            "datatables.net-buttons-bs": "^2.4.1",
            "datatables.net-buttons-bs5": "^2.4.1",
            "jquery": "^3.7.0"
        }
    }
    
    
  • colincolin Posts: 15,118Questions: 1Answers: 2,583

    Nice, thanks for sharing,

    Colin

Sign In or Register to comment.