Rails 6 + yarn + datatables issue - ruby-on-rails

I bumped into troubles with webpacker. I'm using Rails 6.beta3 and trying to add Datatables to my app. My steps:
yarn add datatables.net-dt
then in app/javascript/packs/application.js:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import 'bootstrap/dist/js/bootstrap';
import 'popper.js/dist/popper.js';
require( 'datatables.net-dt' )();
config/webpack/environment.js:
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
module.exports = environment
environment.plugins.append('Provide', new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
Popper: ['popper.js', 'default']
}))
After this if load a page I get an error in js console:
Uncaught TypeError: Cannot set property '$' of undefined
at DataTable (jquery.dataTables.js:129)
jquery.dataTables.js:
var DataTable = function DataTable(options) {
this.$ = function (sSelector, oOpts) { // <---------error is here. Turns out this is not defined variable
return this.api(true).$(sSelector, oOpts);
};
Any ideas? Thanks in advance

Opps. Its my bad. I found the issue. At the end of app/javascript/packs/application.js there was an error. I thought it didn't make impact on overall performance, but after eliminating of this things started working properly.

my app.js
// that code so it'll be compiled.
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import 'bootstrap/dist/js/bootstrap';
require('select2');
import 'popper.js/dist/popper.js';
require("chart.js");
require("leaflet");
import $ from 'jquery';
window.jQuery = $;
window.$ = $;
// var dt = require( 'datatables.net-dt' );
// var dt = require( 'datatables.net-bs4' )( window, window.$ );
require('datatables.net-bs4');
var moment = require('moment');
window.moment = moment
require('bootstrap-daterangepicker');
var feather = require('feather-icons/dist/feather.js')
function onready() {
feather.replace()
console.log('onready')
}
$(document).on('ready turbolinks:load', onready);
/* globals Chart:false, feather:false */
(function () {
feather.replace()
}())

My app/javascript/packs/application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import 'bootstrap/dist/js/bootstrap';
import 'popper.js/dist/popper.js';

My application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import 'bootstrap/dist/js/bootstrap';
import 'popper.js/dist/popper.js';
require( 'datatables.net-dt' )();
And the result is
jquery.dataTables.js:129 Uncaught TypeError: Cannot set property '$' of undefined
at DataTable (jquery.dataTables.js:129)
at Module../app/javascript/packs/application.js (application.js:38)
at __webpack_require__ (bootstrap:19)
at bootstrap:83
at bootstrap:83
The odd thing is the bootstrap part of the stack!

Related

Accessing Rails with webpacker

I'm trying to implement what is described here on my Rails 6.1 app (with webpacker).
"dependencies": {
"#rails/ujs": "^6.1.3",
"#rails/webpacker": "5.4.3",
...
}
My packs/application.js looks like
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
// require("channels")
document.addEventListener("turbolinks:load", () => {
const remoteLinks = Array.from(document.querySelectorAll("a[data-remote='true']"))
remoteLinks.forEach(function(element) {
element.dataset.url = element.href
element.href = "javascript:void(0);"
})
Rails.href = function(element) {
return element.dataset.url || element.href
}
})
And I get Uncaught ReferenceError: Rails is not defined.
What am I missing here?
UPDATE
I was missing
import Rails from "#rails/ujs"

Rails 6.1 webpacker reduce unused JavaScript

I have a rails 6.1 app using webpacker and tailwind.
My page load speed test shows a bad loading speed and suggests to "Reduce unused JavaScript". Sidenote: it also suggests to "Reduce unused CSS". So purge seems not to work. But on the page where I perform the page load speed test, the chartkick/chart.js is not even used. So it seems like it is loading every JS library, despite it is not even used on a page.
Here is my packs/application.js file:
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
import Rails from "#rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "#rails/activestorage"
import "channels"
import "chartkick/chart.js"
import ahoy from "ahoy.js"
import { initMapbox } from '../packs/init_mapbox'
require("stylesheets/application.scss")
require("../stylesheets/components/pagination.scss")
require("../stylesheets/components/vacancy_description.scss")
Rails.start()
Turbolinks.start()
ActiveStorage.start()
document.addEventListener('turbolinks:load', () => {
initMapbox();
})
Here is my tailwind.config.js file:
const colors = require('tailwindcss/colors')
module.exports = {
purge: {
enabled: process.env.NODE_ENV === "production"
content: ['./app/views/**/*.html.erb', './app/javascript/**/*.js',],
},
darkMode: false, // or 'media' or 'class'
theme: {
colors: {
transparent: 'transparent',
current: 'currentColor',
black: colors.black,
white: colors.white,
gray: colors.trueGray,
indigo: colors.indigo,
red: colors.rose,
yellow: colors.amber,
green: colors.emerald
},
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
Hope someone can help!

Cocoon callbacks aren't firing on Rails6

My problem is similar to the one described in https://github.com/nathanvda/cocoon/issues/399.
Adding and removing nested fields works, but callbacks don't fire.
application.js:
import Rails from "#rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "#rails/activestorage"
import "channels"
// Bootstrap
require("bootstrap")
// Toastr
global.toastr = require("toastr")
// Cocoon
require("#nathanvda/cocoon")
import "../stylesheets/application"
require("packs/ingredient")
Rails.start()
Turbolinks.start()
ActiveStorage.start()
ingerdient.js
$(document).on('ready page:load turbolinks:load', function() {
$("a.add_fields")
.on('cocoon:before-insert', function () {
console.log('before insert');
})
.on('cocoon:after-insert', function () {
console.log('after insert');
})
.on("cocoon:before-remove", function () {
console.log('before remove');
})
.on("cocoon:after-remove", function () {
console.log('after remove');
});
$("a.add_fields").on('click', function(){
console.log('looks like clicking works...');
});
});
Console output:
Event Listeners:
jquery.js:4487:
Maybe the problem is double jQuery but I'm newbie and I can't figure it how solve that.
Thanks.
The OP registered the callbacks on the link, but it is recommended to register them on the container object.
Quoting from the cocoon documentation:
Note that for the callbacks to work there has to be a surrounding container to which you can bind the callbacks.

Rails Webpacker - How to access objects defined in webpack entry file from views [HTML file]

I have a Rails 6 application and using Webpacker for assets.
I have the following code in file app/javascript/packs/application.js :
export var Greeter = {
hello: function() {
console.log('hello');
}
}
And I have the following script in one of my view (HTML) file:
<script>
$(document).ready(function(){
Greeter.hello();
});
</script>
Note: I am using JQuery and it is working fine.
I am getting the following error:
Uncaught ReferenceError: Greeter is not defined.
How can we use libraryTarget and library to expose the bundled modules, so that it can be accessed from HTML files as well ?
Or, is there any other way of doing it using Rails Webpacker ?
Any help would be much appreciated!
To do this without directly mutating the window object in your application code, you'll want to export Greeter as a named export from your application.js pack and extend the Webpack config output to designate the library name and target var (or window will also work).
// config/webpack/environment.js
environment.config.merge({
output: {
library: ['Packs', '[name]'], // exports to "Packs.application" from application pack
libraryTarget: 'var',
}
})
// app/javascript/packs/application.js
export {
Greeter
}
<script>
$(document).ready(function(){
Packs.application.Greeter.hello();
});
</script>
The library name is arbitrary. Using the [name] placeholder is optional but allows you to export to separate modules if you're using multiple "packs".
As I cannot comment rossta's answer, here is what I had to do. My default config was:
// config/webpack/environment.js
const { environment } = require('#rails/webpacker')
module.exports = environment
and I just had to add the additionnal config in it:
// config/webpack/environment.js
const { environment } = require('#rails/webpacker')
environment.config.merge({
output: {
library: ['Packs', '[name]'], // exports to "Packs.application" from application pack
libraryTarget: 'var',
}
})
module.exports = environment
After that, as mentioned by rossta, each symbol which is exported in app/javascript/packs/application.js can be accessed from the DOM as Packs.application.<symbol>.
in app/javascript/packs/application.js:
import Greeter from '../greeter.js'
Greeter.hello()
and in app/javascript/greeter.js:
export default {
hello : function(){
console.log('hello')
}
}
I could fix the issue exposing Greeter object to window as follows:
export var Greeter = {
hello: function() {
console.log('hello');
}
}
window.Greeter = Greeter;
However, I am still looking for a Webpack way of accomplishing this.

How to use Material Components for the web with Webpacker

Rails 5.2.1 webpacker4.0
app/javascript/application.scss
#import "material-components-web/material-components-web";
I use webpacker with the default setting, but it does not work well.
What additional settings do I need?
I want to use material-components-web in webpacker
err
Module build failed:
#import "#material/base/mixins";
File to import not found or unreadable: #material/base/mixins.
webpack/environment.js
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const vue = require('./loaders/vue')
environment.plugins.append('VueLoaderPlugin', new VueLoaderPlugin())
environment.loaders.append('vue', vue)
module.exports = environment

Resources