rails / webpacker

Use Webpack to manage app-like JavaScript modules in Rails
MIT License
5.31k stars 1.47k forks source link

Rails 6, Webpacker and 3rd party JS library -- $(...) is not a function #2456

Closed ghost closed 4 years ago

ghost commented 4 years ago

Rails 6, Webpacker and Flickity

Result: TypeError: $(...).flickity is not a function

rails webpacker:install, yarn add jquery and yarn add flickity all ran successfully.

HTML

<div class="main-carousel">
  <div class="carousel-cell">...</div>
  <div class="carousel-cell">...</div>
  <div class="carousel-cell">...</div>
</div>

app/javascript/packs/application.js

require("@rails/ujs").start()
require("@rails/activestorage").start()
require("channels")

require("jquery")

require("flickity")
import "flickity/dist/flickity.min.css";

$(".main-carousel").flickity({
  contain: true
});

config/webpack/environment.js

const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.prepend(
  'Provide', new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    flickity: 'flickity/dist/flickity.pkgd.min'
  })
)

module.exports = environment

node_modules/flickity/dist/

% ls -l
total 352
-rw-r--r--  1 dev  dev    2422 Feb  9 20:32 flickity.css
-rw-r--r--  1 dev  dev    1797 Feb  9 20:32 flickity.min.css
-rw-r--r--  1 dev  dev  118713 Feb  9 20:32 flickity.pkgd.js
-rw-r--r--  1 dev  dev   54063 Feb  9 20:32 flickity.pkgd.min.js
rossta commented 4 years ago

I looked at the source code for flickity. Its main file resolves to flickity/js/index.js, not one of the flickity.pkgd*.js files. Those files appear to be for the global environment and contain the code that attaches flickity as a jQuery plugin.

Therefore, it looks like Flickity is meant to be used directly without jQuery in a fully-featured module environment, like you get with Webpack.

In your application code then, you want something like this (adapted from the docs):

import Flickity from 'flickity'

document.addEventListener('DOMContentLoaded', () => {
  const elem = document.querySelector('.main-carousel');
  const flkty = new Flickity(elem, {
    contain: true
  });
})

You can leave jQuery out of this interaction altogether.

ghost commented 4 years ago

Sweet! Thanks a lot @rossta.