jqueryasp.net-corewebpackasp.net-core-mvc

How to setup jQuery in Webpack and use in a .cshtml file?


I am working on setting up webpack in an ASP.NET Core MVC app. After installing jQuery as a dependency, I am getting an error everywhere I am trying to use jQuery:

Uncaught ReferenceError: $ is not defined

When I look at the site.entry.js, I can see that jQuery is being included in the bundle. I have the script tag set to point to that file and I am also bundling Bootstrap and that is working just fine.

The script tag is located inside the _Layout.cshtml:

<head>
    <script src="/dist/site.entry.js" defer></script>
</head>

Inside my site.js, I added the following imports:

// JS Dependencies: Popper, Bootstrap & JQuery
import '@popperjs/core';
import 'bootstrap';
import 'jquery';
// Using the next two lines is like including partial view _ValidationScriptsPartial.cshtml
import 'jquery-validation';
import 'jquery-validation-unobtrusive';

// CSS Dependencies: Bootstrap & Bootstrap icons
import 'bootstrap-icons/font/bootstrap-icons.css';
import 'bootstrap/dist/css/bootstrap.css';

This is the package.json:

{
  "name": "TestProject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@popperjs/core": "^2.11.8",
    "bootstrap": "^5.3.3",
    "bootstrap-icons": "^1.11.3",
    "jquery": "^3.7.1",
    "jquery-validation": "^1.21.0",
    "jquery-validation-unobtrusive": "^4.0.0"
  },
  "devDependencies": {
    "css-loader": "^7.1.2",
    "eslint": "^9.5.0",
    "expose-loader": "^5.0.0",
    "mini-css-extract-plugin": "^2.9.1",
    "style-loader": "^4.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^5.93.0",
    "webpack-cli": "^5.1.4"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  }
}

And this is the webpack.config.js:

const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const webpack = require('webpack');

module.exports = {
    entry: {
        site: './wwwroot/js/site.js'
    },
    output: {
        filename: '[name].entry.js',
        path: path.resolve(__dirname, 'wwwroot', 'dist'),
        clean: true
    },
    devtool: 'source-map',
    mode: 'development',
    plugins: [
        new MiniCssExtractPlugin(),   
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery'
      })],
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif|webp)$/i,
                type: 'asset/resource',
                generator: {
                    // keep original filenames and copy images to dist/images/
                    filename: 'images/[name][ext]'
                }
            },
            {
                test: /\.(eot|woff(2)?|ttf|otf)$/i,
                type: 'asset/resource',
                generator: {
                // keep original filenames and copy fonts to dist/fonts/
                filename: 'fonts/[name][ext]'
                }
            },
            {
                // Exposes jQuery for use outside Webpack build
                test: require.resolve('jquery'),
                loader: "expose-loader", options: { exposes: ["$", "jQuery"], }
              }
        ]
    }
};

I am trying to use jQuery inside some of my other views. For example, I have a Profile.cshtml that has a script tag at the bottom of the file.

// html code is above the script tag
<script type="text/javascript">
    const accountDetailsModal = document.getElementById("editAccountModal");
    const selectedState = "@Model.AccountProfile.State";
    accountDetailsModal.addEventListener("show.bs.modal", event => {
        let stateField = document.getElementById("State");
        stateField.value = selectedState;
    });


    $("#editAccountModal").on("submit", "#form-accountprofileedit", function (e) {
        e.preventDefault();
        var form = $(this);
        $.ajax({
            url: form.attr("action"),
            method: form.attr("method"),
            data: form.serialize(),
            success: function (partialResult) {
                $("#formContent").html(partialResult);
            }
        });
    });
</script>

I'm not sure what I am doing wrong or what I could be missing.


Solution

  • Remove defer can fix the issue.

    <head>
        <script src="/dist/site.entry.js" ></script>
    </head>