How to use Javascript via Webpacker in Rails 6 Discussion
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.
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?
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 solved by upgrading stimulus, for example put "stimulus": "^2.0.0",
in package.json and run yarn upgrade stimulus
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:872in 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.
When I set this up everything works but when I go to another page then hit the back button to go back to the form with flatpickr on it it duplicates the date field and continues to do so each time you transition out then back.
I also cloned this projects repo and it also does it with it. Any thoughts on why it is doing this?
Has anybody experienced yarn not finding flatpicker?
yarn add flatpicker
yarn add v1.22.4
[1/4] 🔍 Resolving packages...
error An unexpected error occurred: "https://registry.yarnpkg.com/flatpicker: Not found".