In my Rails 5 app, if I load Bootstrap 4 using a header <script>
tag from a CDN, everything works fine. But when I install Bootstrap (and its dependencies) to node_modules
, and serve it with Webpack, it mostly still works... but the jQuery methods Bootstrap is supposed to add like $(foo).modal()
or $(bar).popover()
give errors in the JavaScript console:
Uncaught TypeError: $(...).modal is not a function
Uncaught TypeError: $(...).popover is not a function
There are several other StackOverflow questions about errors like that, but none of the solutions listed there work for me. The most common cause seems to be jQuery getting included twice, but I'm pretty sure I'm not doing that; when I remove the jQuery import
from app/javascript/packs/application.js
(see below), then all of jQuery stops working. jQuery is not in my Gemfile
as a gem, and the word "jquery" only appears in the files I've shown below.
I'm loading in the correct order (see below; jQuery and Popper before Bootstrap). I'm using a document-ready wrapper around the method calls (see below), so I'm not calling them too soon. The Bootstrap CSS loads fine, and DOM elements using data-toggle="modal"
display the modal correctly:
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Show Modal
</button>
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<!- ... ->
</div>
</div>
</div>
...it's only when methods like $(foo).modal()
etc. are called in JS code that I get the error:
<script>
$(function () {
$('#exampleModal').on('shown.bs.modal', function (e) {
console.log('modal shown!');
});
$('#otherModalButton').click(function (e) {
e.preventDefault();
console.log('clicked!');
$('#exampleModal').modal('show'); // <- "not a function" error
return false;
});
});
</script>
My setup:
Node.js v10.20.1
ruby 2.5.8p224 (2020-03-31 revision 67882) [x86_64-linux]
Rails 5.2.4.2
package.json: "@rails/webpacker": "4.2.2",
package.json: "bootstrap": "4.4.1",
package.json: "jquery": "3.4.1",
package.json: "popper.js": "1.16.1"
package.json: "webpack-dev-server": "^3.11.0"
// config/webpack/development.js
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
// config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
environment.plugins.append(
'Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery', // taking this out doesn't fix it
Popper: ['popper.js', 'default']
})
)
module.exports = environment
<!DOCTYPE html>
<!-- app/views/layouts/application.html.erb -->
<html>
<head>
[...]
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= javascript_pack_tag 'application' %>
<%# if I load with these two lines (instead of in app/javascript/packs/application.js), it all works: %>
<%# javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.1/umd/popper.min.js' %>
<%# javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.min.js' %>
</head>
<body>
<%= yield %>
</body>
</html>
// app/javascript/packs/application.js
import 'jquery/src/jquery'
import 'popper.js/dist/esm/popper'
import 'bootstrap/dist/js/bootstrap'
import '../stylesheets/application'
// app/javascript/stylesheets/application.scss
@import "~bootstrap/scss/bootstrap";
I've manually reviewed the JS pack that webpack-dev-server
is producing; I can see Bootstrap in there. The Webpack output in the console shows the pack successfully compiled with no errors. I've tried this on both Chrome (ChromeOS and MacOS) and Firefox (MacOS).
Any ideas for what I can try?