javascriptjquerylaravelnpmvite

What's the proper way to load dependent NPM modules?


I'm having quite the trouble loading dependent modules like: jQuery + jQueryUI + jQuery-tablesorter I'm always getting JS error - not being able to find/load some of the components by the browser - npm modules imported into vite. This case the jQuery tablesorter is not found, giving the JS error in the debug console. The PHP framework is Laravel 11 + Blade + Vite.

The layout file contains jQuery in the head section (I had nearly the same JS loading issues, so I've kept is there since I use it in many places):

      <script type="text/javascript" src="{{ asset('assets/jquery/jquery.min.js') }}"></script>
        <script type="text/javascript">
            window.jQuery = window.$;
        </script>

        @stack('header_scripts')

        <!-- Scripts -->
        @vite(['resources/css/app.css', 'resources/js/app.js'])

The app.js:

import Alpine from 'alpinejs';

window.Alpine = Alpine;

Alpine.start();

import 'jquery-datetimepicker';
// datetimepickerFactory($);

import 'tablesorter/dist/css/theme.dropbox.min.css';
import 'tablesorter/dist/js/jquery.tablesorter.combined.min.js';

And in my view file I've this:

@push('scripts')
    <script type="module">
        document.addEventListener("DOMContentLoaded", () => {
            jQuery("#users-table").tablesorter({
              ....
            });
        });
            </script>
    @endpush

The stack('scripts') is the last line before the closing /body tag in the layout file.

Upon page render I get enrollments:345 Uncaught TypeError: jQuery(...).tablesorter is not a function at HTMLDocument.<anonymous> and I can't figure out why (npm run dev is running, also tried running 'npm run build')

Please help me understand the whole loading mechanism and how to fix the issue properly.

(I've tried putting jQuery into app.js, but than I was not able to load jquery-ui properly - seemed that jQuery-ui was not able to recognize jQuery upon init so it bailed out)


Solution

  • I managed to figure out, but I don't fully understand why it's working like this. This post gave me a clue.

    The final - and working - setup is like this:

    The layout blade's <head> contains the @vite reference and noting more (related to the issue):

            @vite(['resources/css/app.css', 'resources/js/bootstrap.js', 'resources/js/app.js'])
    

    bootstrap.js only contains axios and jQuery inclue:

    import axios from 'axios';
    window.axios = axios;
    
    window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
    
    import $ from 'jquery';
    window.$ = window.jQuery = $; // Make jQuery globally available
    

    And the relevant part of app.js (contains more stuff, but I've left some as a working example)

    import Alpine from 'alpinejs';
    
    window.Alpine = Alpine;
    
    Alpine.start();
    
    import 'jquery-ui/dist/jquery-ui';
    
    import 'jquery-datetimepicker';
    // datetimepickerFactory($);
    
    import 'tablesorter/dist/css/theme.dropbox.min.css';
    import tablesorter from 'tablesorter/dist/js/jquery.tablesorter.combined.min.js';
    window.tablesorter = tablesorter;
    
    
    // import 'jqueryui/jquery-ui.theme.min.css';
    import 'jquery-datetimepicker/jquery.datetimepicker.css';
    import 'pretty-checkbox/dist/pretty-checkbox.min.css';
    
    import 'formBuilder/dist/form-builder.min.js';
    import 'formBuilder/dist/form-render.min.js';
    // window.formbuilder = formbuilder;
    // window.formrender = formrender;
    
    import tippy from 'tippy.js';
    window.tippy = tippy;
    import 'tippy.js/dist/tippy.css';
    import 'tippy.js/themes/material.css';
    
    import '@popperjs/core';
    
    // Flowbite for TailWind tabs
    import 'flowbite';
    
    

    In the relevant blade the tablesorter must be included as a module:

    @push('scripts')
        <script type="module">
            document.addEventListener("DOMContentLoaded", () => {
              jQuery("#enrollments-table").tablesorter();
            });
        </script>
    @endpush
    

    Since nearly all of the other suggestions showed vite.config.js, here it is for completness:

    import { defineConfig } from 'vite';
    import laravel from 'laravel-vite-plugin';
    
    export default defineConfig({
        plugins: [
            laravel({
                input: [
                    'resources/css/app.css',
                    'resources/js/app.js',
                ],
                refresh: true,
            }),
        ],
        resolve: {
            alias: {
                '@' : '/resources/js',
                jQuery: 'jquery',
                '$': 'jquery',
            },
        },
    });
    

    Note: setting an alias in vite.config.js is not relevant for the problem. Aliases are there to make imports in app.js easier.

    The above setup is working for me, all the imports are ok, however I don't know why it's working like this (I've the suspicion that it's something with vite). Please be so kind and extend/comment if you know, I'm really willing to know!