I try to organise my frontend as I used to do in Rails 5. I had some js file with functions and used this functions in different places of code up to my needs. But in Rails 6 work with js is quite different. Anyway, I think I got the main idea about packs and webpacker. But how to use custom js functions? Write it in one file and use in another? There is should be the way to do it.
For example, I have some custom js pack:
app/javascript/packs/custom_pack_with_functions.coffee:
console.log 'hey'
@hi = () ->
console.log 'HI'
And I expect that hi
function will be available in my view.
some_view.html.slim:
= javascript_pack_tag 'custom_pack_with_functions'
javascript:
hi()
But when I come to appropriate page, I see in console only following messages:
hey
ReferenceError: hi is not defined
How to define hi
function to use it from anywhere?
Webpack does not make modules available to the global scope by default. Here are a few ways you can do it:
Assign the function to the global window
object, i.e., window.hi = function() { ... }
. I don't like side effects like this in a lot of places so it's my least favorite option but perhaps the easiest to understand.
You could look at using expose-loader
. This would mean customizing your webpack config to "expose" selected functions from selected modules to the global scope. It could work well for a handful of cases but would get tedious for many use cases.
Export selected functions from your entrypoint(s) and configure webpack to package your bundle as a library. This is my favorite approach if you prefer to call global functions from the view. I've written about this approach specifically for Webpacker on my blog.
// app/javascript/packs/application.js
export * from '../myGlobalFunctions'
// config/webpack/environment.js
environment.config.merge({
output: {
// Makes exports from entry packs available to global scope, e.g.
// Packs.application.myFunction
library: ['Packs', '[name]'],
libraryTarget: 'var'
},
})
:javascript
Packs.application.hi()
Don't use global functions at all; use a different mechanism to trigger the function from within your webpack JS, such from within an event listener for the given page or in the presence of a given element.
// app/javascript/initializer.js
import hi from '../hi';
document.addEventListener('DOMContentLoaded', () => {
if ( /* some logic for my page is true */ ) {
hi()
}
});