Skip to main content
Ask A Question
Notifications
You’re not receiving notifications from this thread.
Subscribe

How to use Javascript via Webpacker in Rails 6 Discussion

General • Asked by Chris Oliver

Alternatively to the webpacker way of require the flatpicker css in the packs/application.js you can do someting like:

/*
 *= require flatpicker/dist/flatpicker
 */

In your rails app/assets/stylesheets/application.css (or other) manifest file.


Chris, isn't it important to include the following line in the main entry pack:

import "babel-polyfill";

Without it, as far as I understand, Babel will not transpile the ES6 code and there should be issues with current browsers, right?


Hi Chris, regarding Yarn. Do we have to use Yarn in Rails 6 or can we use NPM and the package.json file? Is there an advantage to using Yarn vs NPM? Thanks!

You can use either but stick to one. Yarn I believe was created by facebook as an alternative to NPM due to some issues they had. Yarn is also faster I believe.

But stick to one as you might run into issues when (accidentally) using both. I use Yarn—for the speed.


Awesome, I got it all working on a standard form but the calendar doesn't display on an Ajax form. I assume my "_edit_info.js" needs to call flatpickr. Or what's the best way to go about this? Thanks

Figured it out:
document.getElementById("userinfodiv").innerHTML = "<%= j render 'editinfo' %>"

flatpickr("[data-behavior='flatpickr']",{
altInput: true,
altFormat: "F j, Y",
dateFormat: "Y-m-d",
})


I have created a new Rails 6 (rc1) with the latets version of webpacker (4.0.6) and when I run my app I get the following error

decode.js:32 Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
    at Module.<anonymous> (decode.js:32)
    at Module../node_modules/querystring-es3/decode.js (decode.js:88)
    at __webpack_require__ (bootstrap:19)
    at Object../node_modules/querystring-es3/index.js (index.js:3)
    at __webpack_require__ (bootstrap:19)
    at Object.<anonymous> (client:6)
    at Object../node_modules/webpack-dev-server/client/index.js?http://localhost:3035 (client:337)
    at __webpack_require__ (bootstrap:19)
    at Object.0 (log.js:47)
    at __webpack_require__ (bootstrap:19)

Anyone else have expirienced the same issue?

Cheers,
Sig


Do you recommend building a new app in Rails 6 rc1 today and upgrading to 6.0 when it releases or starting with 5.2.3? Will one be easier than the other to update?


Is it sensible to use Tailwind together with Bootstrap, or is it more of a replacement?


really great episode, this was everything i needed to install what i was working on, tablesort javascript library.

only thing was, that import didn't work and i had to use require instead. not sure why
var Tablesort = require('tablesort')

the finished code in app/javascript/packs/application.js

var Tablesort = require('tablesort')
document.addEventListener("turbolinks:load", () => {
  document.querySelectorAll('[data-tablesort]').forEach(
    function(table) { new Tablesort(table) }
  )
})

I followed this and am getting the following errors:

definition.js:34 Uncaught TypeError: constructor.bless is not a function
    at blessControllerConstructor (definition.js:34)
    at blessDefinition (definition.js:28)
    at new Module (module.js:9)
    at Router.loadDefinition (router.js:63)
    at application.js:213
    at Array.forEach (<anonymous>)
    at Application.load (application.js:212)
    at Module../app/javascript/controllers/index.js (index.js:9)
    at __webpack_require__ (bootstrap:19)
    at Module../app/javascript/packs/application.js (application.js:1)

Anyone else see this?


I'm trying to follow the instructions from this video for a different library, simplemde. I'm not very clear on how to use the variable as you called it in the application.js file:

import simplemde from "simplemde"
require("simplemde/dist/simplemde.min.css")

document.addEventListener("turbolinks:load", () => {
    simplemde() // <- Not sure how to use this
})

When I just call simplemde() at that point, I get:

Uncaught TypeError: Cannot set property 'options' of undefined
at SimpleMDE (simplemde.js:1417)
at HTMLDocument. (application.js:26)
at Object../node_modules/turbolinks/dist/turbolinks.js.e.dispatch (turbolinks.js:75)
at r.notifyApplicationAfterPageLoad (turbolinks.js:994)
at r.pageLoaded (turbolinks.js:948)
at turbolinks.js:872

in the Javascript console.

How can I determine what it's expecting?

Just instantiating a new SimpleMDE object doesn't work either:

document.addEventListener("turbolinks:load", () => {
    var smde = new SimpleMDE()
})

Uncaught ReferenceError: SimpleMDE is not defined
at HTMLDocument. (application.js:26)
at Object../node_modules/turbolinks/dist/turbolinks.js.e.dispatch (turbolinks.js:75)
at r.notifyApplicationAfterPageLoad (turbolinks.js:994)
at r.pageLoaded (turbolinks.js:948)
at turbolinks.js:872

Thanks


@Chris,

Can you shed any light on how the require or import in the pack manifest file works?

Here's a real-life example from a project I'm upgrading to Rails 6 with webpacker to manage the assets.

I'm using the pikaday JS library for a calendar, wrapped with a Datepicker JS class (to make refactoring easier if I someday change the calendar library), and this class is used in a StimulusJS controller.

Because the controller file imports the Datepicker class, and the Datepicker class imports the Pikaday library, I don't need to import/require pikaday or my Datepicker in the application.js pack file at all, and I'm curious why that is?

Also, if I import Datepicker from "custom/datepicker" in the application.js pack file, why can't I reference Datepicker in other classes I import in the manifest, such as the controllers? Why do the controllers have to manually import Datepicker in order to reference it?

It seems like theres a lot of manually importing classes when I switched to webpacker...in the asset pipeline, I could reference these classes anywhere without having to manually import them all the time. Was it because they were somehow automatically being added to a global namespace?

I understand the principles behind avoiding over-populating the global namespace, but having to manually import every single class I want to use in every single file seems a little overkill. Have you happened upon anything that can help with this? (I've looked at webpacker's ProvidePlugin, and it doesn't seem to help with instantiating new classes, only if you're referencing the global itself, such as $ or jQuery).

app/javascript/packs/application.js

require("controllers") // loads the default index.js which loads all JS files in directory

app/javascript/controllers/assignment_form_controller.js

import { Controller } from "stimulus"
import Datepicker from "custom/datepicker"

export default class extends Controller {
  initialize() {
      new Datepicker(document.getElementById("some_id"))
    }
}

app/javascript/custom/datepicker.js

import Pikaday from "pikaday"

export default class Datepicker {
  constructor(element, options) {
    if (options == null) { options = {} }

    // Store DOM element for reference
    this.element = element

    // Do not re-run on elements that already have datepickers
    if (this.element.datepicker === undefined) {
      options = Object.assign({},
        this.defaultOptions(),
        options
      )

      const picker = new Pikaday(options)

      // Store picker on element for reference
      this.element.datepicker = picker

      return picker
    } else {
      console.log("Datepicker already attached")
      return
    }
  }

  // Overridden by `options` in constructor
  defaultOptions() {
    return {
      field: this.element,
      format: "M/D/YYYY",
      bound: true,
      keyboardInput: false,
      showDaysInNextAndPreviousMonths: true
    }
  }
}

After some more digging and some answers on SO, I realized I've been thinking that the way I did things in sprockets can transfer to webpacker, which is not true.

Sprockets essentially combines all my required JS files into a single file with a global namespace, which is why I could reference classes/functions right after they were required (thus the order of files mattered).

Webpacker also creates a single pack file of JS code (or as many packs as you have), but the pack is not a combined file of JS, but rather a combination of es6 modules, such that each module is completely namespaced and separate from others. Thus, to reference my Datepicker class in another es6 module, I have to manually import Datepicker from "datepicker_file" in each separate class (noting that webpacker will ensure only 1 copy of the JS code is actually included).

That means only the bare minimum of entry-level JS code needs to go in the webpacker pack files. For instance, if I use the environment.js ProvidePlugin to make jquery global, you don't actually need to require("jquery") in the pack file.

And since my Datepicker class includes the pikaday library, I don't need to add pikaday at all to my pack file because webpacker will eventually include pikaday because it's a dependency.

I think webpacker will be better overall when I finally understand it, but it is going to require a LOT of refactoring for several of my applications, because they were written on the assumption that if a file was required it was accessible in the global namespace.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 31,575+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.