Cocoon callbacks aren't firing on Rails6 - ruby-on-rails

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.

Related

how to use react-leaflet-easyprint with react-leaflet 3

react-leaflet-easyprint has examples and docs for react-leaflet v1 and v2.
However, out of the box it seems to be incompatible with v3.
How can i make it work with v3?
This is how i did it.
I used the following packages instead and maybe they will work the same way
// package.json
"leaflet-easyprint": "^2.1.9",
"react-leaflet": "^4.0.0",
// MapPrint.js
import L from 'leaflet';
import 'leaflet-easyprint';
import { useEffect } from 'react';
import { useMap } from 'react-leaflet';
function MapPrint(props) {
const map = useMap();
useEffect(() => {
const control = L.easyPrint({
...props
});
map.addControl(control)
return () => {
map.removeControl(control);
}
}, [map]);
return null;
}
export default MapPrint
after that you could use it like this (inside MapContainer from the new react-leaflet):
<MapContainer zoom={3} >
<MapPrint position="topleft" sizeModes={['Current', 'A4Portrait', 'A4Landscape']} hideControlContainer={false} title="Print" />
<MapPrint position="topleft" sizeModes={['Current', 'A4Portrait', 'A4Landscape']} hideControlContainer={false} title="Export as PNG" exportOnly />
</MapContainer>
So what i am saying is to use the js implementation of easyprint instead of using react wrapped version and wrap it by yourself.

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!

Using ActionText without webpack

I try to implement ActionTest with old way asset pipeline (without Webpack) on rails 6
Almost all is good, except loading of #rails/actiontext
in my application.js I've
//= require trix
//= require #rails/actiontext
The riche editor appear, I can change bold/italic text, but can't add image (not uploaded)
I've an JS error : Uncaught SyntaxError: Cannot use import statement outside a module
on line : import { AttachmentUpload } from "./attachment_upload" from attachment_uplaod.js in actiontext.
Any way to achieve this without webpack?
thanks
I don't know what will be the official way, but I did it this way as I'm waiting for an updated install generator:
Prerequisites
hotwire-rails
CSS
Copy the CSS file in your asset pipeline (https://github.com/basecamp/trix/tree/main/dist)
JS Libraries
In app/assets/javascripts/libraries create these two files
Updated content may be found on https://www.skypack.dev
// app/assets/javascripts/libraries/actiontext#6.1.4.js
export * from 'https://cdn.skypack.dev/pin/#rails/actiontext#v6.1.4-znF92tREya92yxvegJvq/mode=imports/optimized/#rails/actiontext.js';
export { default } from 'https://cdn.skypack.dev/pin/#rails/actiontext#v6.1.4-znF92tREya92yxvegJvq/mode=imports,min/optimized/#rails/actiontext.js';
// app/assets/javascripts/libraries/trix#1.3.1.js
export * from 'https://cdn.skypack.dev/pin/trix#v1.3.1-EGGvto9zyvcAYsikgQxN/mode=imports/optimized/trix.js';
export { default } from 'https://cdn.skypack.dev/pin/trix#v1.3.1-EGGvto9zyvcAYsikgQxN/mode=imports,min/optimized/trix.js';
Import through Stimulus
In app/assets/javascripts/controllers create this file
//app/assets/javascripts/controllers/trix_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
connect() {
import("actiontext").catch(err => null)
import("trix").catch(err => null)
}
}
On pages where trix/action_text should be loaded, add a data-controller="trix" to the relevant div
And voilĂ  !
https://github.com/rails/rails/issues/41221#issuecomment-871853505
Got Action Text working by copying the actiontext scripts into a custom file, and removing the imports and exports.
And second, you will need to require activestorage in your application.js to make use of DirectUpload.
application.js
//= require trix
//=# require #rails/actiontext
//= require activestorage
//= require actiontext
actiontext.js
// Copied from node_modules/#rails/actiontext/app/javascript/actiontext/attachment_upload.js
class AttachmentUpload {
constructor(attachment, element) {
this.attachment = attachment;
this.element = element;
// Requires `require activestorage` in application.js
this.directUpload = new ActiveStorage.DirectUpload(
attachment.file,
this.directUploadUrl,
this
);
}
start() {
this.directUpload.create(this.directUploadDidComplete.bind(this));
}
directUploadWillStoreFileWithXHR(xhr) {
xhr.upload.addEventListener("progress", event => {
const progress = (event.loaded / event.total) * 100;
this.attachment.setUploadProgress(progress);
});
}
directUploadDidComplete(error, attributes) {
if (error) {
throw new Error(`Direct upload failed: ${error}`);
}
this.attachment.setAttributes({
sgid: attributes.attachable_sgid,
url: this.createBlobUrl(attributes.signed_id, attributes.filename)
});
}
createBlobUrl(signedId, filename) {
return this.blobUrlTemplate
.replace(":signed_id", signedId)
.replace(":filename", encodeURIComponent(filename));
}
get directUploadUrl() {
return this.element.dataset.directUploadUrl;
}
get blobUrlTemplate() {
return this.element.dataset.blobUrlTemplate;
}
}
// Copied from node_modules/#rails/actiontext/app/javascript/actiontext/index.js
addEventListener("trix-attachment-add", event => {
const { attachment, target } = event;
if (attachment.file) {
const upload = new AttachmentUpload(attachment, target);
upload.start();
}
});
This still uses ES6 syntax, so if you want to support older browsers and aren't using Babel, you might want to rewrite or transpile this to ES5.

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.

Rails 6 + yarn + datatables issue

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!

Resources