I'm switching a Ruby on Rails application from sprockets to webpacker. I'm getting an error for inline javascript in my existing slim templates, JQuery is not defined. I tried adding a require for jquery in the application.html.slim but that doesn't help.
doctype html
html
head
title My Application
meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"
meta name="have-i-been-pwned-verification" value="..." /
meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"
= include_gon(watch: true)
= stylesheet_link_tag 'application', media: 'all'
= javascript_pack_tag 'application'
= javascript_include_tag 'jquery'
= csrf_meta_tags
body
= render 'layouts/navigation/main_nav', disable_search: true
= flash_messages
main.application
= yield
Uncaught ReferenceError: $ is not defined
.comments-wrapper.collapse.show.comments-collapse#comments
.row.comments
.col-sm-12
.row.comment-post
.col-sm-12.parent-comment-form
- if user_signed_in?
= render 'we_vote/comments/form', commentable: commentable
- unless local_assigns[:limit_comments].present?
.row.filter-row
.col-sm-12.col-lg-2.offset-md-1.offset-sm-0.text-right
.sort-label sort by
.col-lg-4.col-sm-12.mb-2
select.sort-select
option Popular
option Reply Number
option Upvote Number
option Newest
.col-lg-4.offset-lg-1.col-sm-12.offset-md-0
select.sort-select
option Today
option Last Week
option This Week
.row
.col-sm-12.comments-container class="#{'empty' unless commentable.comments.any?}"
ul.comments-list
- if local_assigns[:limit_comments]
- commentable.comments.top_level.by_popularity.limit(limit_comments).each do |comment|
= render 'we_vote/comments/comment', comment: comment, commentable: commentable, limited: true
- else
- commentable.comments.top_level.by_popularity.each do |comment|
= render 'we_vote/comments/comment', comment: comment, commentable: commentable
= render 'we_vote/comments/report_comment_modal'
javascript:
$(function(){
var commentId = "#{ params[:comment_id] }";
if(commentId) {
var $comment = $('#' + commentId);
$comment.get(0).scrollIntoView();
$comment.addClass('notified');
}
});
const { environment } = require("@rails/webpacker");
const erb = require("./loaders/erb");
const jquery = require("./plugins/jquery");
const webpack = require("webpack");
environment.plugins.append(
"Provide",
new webpack.ProvidePlugin({
"window.Tether": "tether",
Popper: ["popper.js", "default"],
})
);
const aliasConfig = {
jquery: "jquery-ui-dist/external/jquery/jquery.js",
"jquery-ui": "jquery-ui-dist/jquery-ui.js",
};
environment.config.set("resolve.alias", aliasConfig);
environment.plugins.prepend("jquery", jquery);
environment.loaders.prepend("erb", erb);
module.exports = environment;
const webpack = require("webpack");
module.exports = new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
});
import $ from "jquery";
import autosize from "autosize";
global.$ = $;
global.jQuery = $;
import tether from "tether";
global.Tether = tether;
require("@rails/ujs").start();
require("@rails/activestorage").start();
require("channels");
require("../src/google_analytics");
require("jquery");
require("jquery-ui");
require("../src/autocomplete-rails");
require("tether");
require("popper.js/dist/umd/popper");
require("bootstrap/dist/js/bootstrap");
require("bootstrap-notify/bootstrap-notify");
require("jquery-mask-plugin");
require("select2/dist/js/select2.full.min");
require("jquery-textcomplete");
require("jquery-match-height");
require("jquery-jscroll");
require("../src/facebook");
require("../src/components/comment");
require("../src/components/discussion");
require("../src/components/follow");
require("../src/components/hashtaggable");
require("../src/components/news_feed");
require("../src/components/preview-img");
require("../src/components/question");
require("../src/components/remote_buttons");
require("../src/components/report");
require("../src/components/search");
require("../src/components/sidebar");
require("../src/components/upvote");
require("../src/components/vote");
require("../src/components/verification");
jQuery.railsAutocomplete.options.delay = 300;
jQuery.railsAutocomplete.options.autoFocus = true;
The problem is that your inline script gets executed before jquery. The reason for that, is because it's inlined in the html, so it's available right away. On the other hand, jquery is not inline so the browser has to make a request before executing it.
There are several options.
Option: wait for jQuery to be loaded
One option is to wrap all you inline code in a function that waits for jQuery to be loaded.
javascript:
document.addEventListener('DOMContentLoaded', function () {
var commentId = "#{ params[:comment_id] }";
if(commentId) {
var comment = document.getElementById(commentId);
commment.classList.add('notified');
}
}, false);
Option: stub jQuery
Another option would be to create a stub (fake) $
function, store all the calls to $
, wait for jQuery to be loaded and replay those calls. More details here