Rails 6 - what is the proper way to load custom JS files? - javascript

I struggle to properly configure and set up my own JS files that contain JS functions for specific tasks.
I went through some articles and found that I need to place my custom JS to the JS packs folder -> app/javascript/packs/currency_calculations.js:
currency_calculations.js:
function convert_curr(from, to) {
...
}
function show_convertion(curr) {
...
}
...
and then I try to add this custom JS file to app/javascript/packs/application.js:
import Rails from "#rails/ujs";
import "#hotwired/turbo-rails";
import * as ActiveStorage from "#rails/activestorage";
import "channels";
import "controllers";
Rails.start();
ActiveStorage.start();
import "stylesheets/application";
// my custom JS file
import "packs/currency_calculations" // I also tried import "currency_calculations" -- same result
I also tried to add the following to the application.html.erb file:
= javascript_pack_tag 'currency_calculations'
It didn't work either.
I am still getting this error:
Uncaught Error: Cannot find module 'currency_calculations'
and when trying to call a function from a view, then:
Uncaught ReferenceError: convert_curr is not defined
What is the correct way to wire this up? I am used from Rails 5 to put all my JS functions to a js file and this file just to add to a app/assets/javascripts/application.js like this:
//= require currency_calculations
and then, in a view, I am able to simply call the wanted JS function like convert_curr("a", "b").
Thank you in advance.

There's a couple of ways to do this in Rails 6.
The first would be to create a custom directory and require it in the application.js file. In this case you could create a directory like this:
app/javascript/custom/currency_calculations.js
Then you would need to require it in your application.js file as such:
// app/javascript/packs/application.js
// ...
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("custom/currency_calculations")
That same method could also be streamlined if you, say, named your custom folder "currency" and then named the .js file index.js.
You could then just call it like this:
require("currency")
Require will look for the index file by default in the folder. But in that scenario, an index file must be present, or it will fail.
The other way to do this, in the event you don't want that JS to be compiled with everything else, is to use the javascript_pack_tag.
In that case, add the js file to your app/javascript/packs directory. Then use the pack tag helper where you need it such as:
<%= javascript_pack_tag 'currency_converter' %>
The last thing I would mention... are you sure there's no other library needed to make it work (such as JQuery)? In that case you would need to install and import that library to your application.js before you called the js file you're trying to execute.

I think the problem is you are not exporting anything in your js file. Try doing this in currency_calculations.js
const funcs = {
convert_curr() { console.log('foo') },
show_convertion() { console.log('bar') },
}
export default funcs;
And then in your code you call them with funcs.convert_curr()
Also it seems that currency_calculation shouldn't be it's own pack (you can think of a pack kinda like what application.js was in sprockets), so better be just a standalone js file outside the packs dir. (could be javascript/currency_calculations.js or javascript/utils/currency_calculations.js)

Some comments that may help you:
1 ) when you put a js file at /packs, it's going to be compiled as a standalone asset you can reference using javascript_pack_tag, so you don't need to add it to the application.js pack
you have two options depending on what you want:
move the file to javascript/src/currency_calculations.js and import it in your application.js as import 'src/currency_calculations' or import '../src/currency_calculations'
use it as a pack, remove it from application.js and use load it like javascript_pack_tag 'currency_calculations'
(you can have the file loaded both in application.js and as a standalone pack, but you'll have the code twice)
2 ) if you want to access the functions from that file in your view, you can't do it just like sprocket does. sprockets adds the content of that file in the global scope while webpacker contains the functions in the bundle context. If you want the functions to be available globally from your views, you have to make them global doing something like
global.convert_curr = function(from, to) {
...
}
(you can also use window.convert_curr = ...)

Related

Laravel Vite Can't Use JS Functions Globally

I'm using Laravel with Vite. But I'm having a trouble. I have multiple JS files and including them whenever I want to use. But I cannot use the function I created in one file in another file.
I have an app.js file looks like this:
import jQuery from 'jquery';
window.$ = jQuery;
import './bootstrap';
import './main.js';
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
And I have a function like this in main.js:
function testFunction() {
alert('test');
}
I put Vite definitions to my layout blade like this:
#vite(['resources/css/app.css', 'resources/js/app.js'])
I have another page contains another JS file like this:
#extends('layouts.app')
#section('content')
#endsection
#section('scripts')
#vite(['resources/js/edit-note-page.js'])
#endsection
And in that JS file I want to use the function that I mentioned in the beginning like this:
testFunction();
But I'm getting an Uncaught ReferenceError: testFunction is not defined error in console. I couldn't figure it out. What is the correct way to do it?
I'm using Laravel with Vite. I have multiple JS files and including them whenever I want to use. But I cannot use the function I created in one file in another file.
Each file is going to be wrapped to actually prevent what you are attempting to do, unless explicitly defined on the window object (notice how window.Alpine = Alpine; is set in your app.js). An alternative and more modern way is to use exports. Here is an example using your file structure.
main.js:
function testFunction() {
alert('test');
}
export default testFunction;
edit-note-page.js:
import testFunction from './path/to/file/name';
...code here
The path to the file in the import does not need the extension .js at the end.
Note: After this change, you no longer need import './main.js'; inside of app.js

Referencing vanilla js file in TypeScript getting file is not a module

I'm trying to import a file into TypeScript that's basically just a js file that you'd put into a tag. I've tried a few different things.
// global.d.ts
declare module 'myfile.js'
Inside of the react file:
// component.tsx
import { foo } from '../lib/myFile.js' // This is saying it is not a module
Inside of the js file, it looks like this a few times so not sure how I need to reference the file:
(function( something ) {
something.Foo = function (){}
}(window.something = window.something || {}));
Any thoughts on how I could use this file? Do I need to go through and declare typings for everything in it?
EDIT: I've added allowJS to my tsconfig but it still doesn't work.
You can only import what is exported from the file.
If your file contains only immediately invoked functions, or top level code, you only need to import the file itself like this:
import '../lib/myFile.js'
This is a little weird, however. I would suggest wrapping everything with a function and exporting then importing that function instead.

Import array of modules (MDBootstrap js modules) into Laravel Mix

I have a fresh Laravel 5.8 installation and I would like to include the js modules from MDBootstrap.
In my bootstrap.js file I have:
require('./mdb/modules');
The ./mdb/modules.js file has:
exports.modules = [
'./js/_intro-mdb-pro.js',
'./js/scrolling-navbar.js',
'./js/vendor/jquery.easing.js',
'./js/vendor/velocity.js',
'./js/vendor/chart.js',
'./js/vendor/wow.js',
... 30 more
];
The compilation works ok, but of course, it doesn't take any effect from these modules include.
I don't know how to include all of them at once as they are not looking like a regular module so I can import like 'import * from 'my-module'
I also tried the ES6 way:
import * as MDBootstrap from './mdb/modules';
but I got the same result: successful compilation without including them into the compiled js file.
The content of these 'modules' doesn't seem to look like a normal module where we export default {} or some other functions or variables. It looks like:
//mdb/js/scrolling-navbar.js
"use strict";
(function ($) {
var SCROLLING_NAVBAR_OFFSET_TOP = 50;
$(window).on('scroll', function () {
var $navbar = $('.navbar');
if ($navbar.length) {
if ($navbar.offset().top > SCROLLING_NAVBAR_OFFSET_TOP) {
$('.scrolling-navbar').addClass('top-nav-collapse');
} else {
$('.scrolling-navbar').removeClass('top-nav-collapse');
}
}
});
})(jQuery);
Even 'exports.modules = [..]' is not familiar to me. I know about 'module.export = ...' but this 'exports' looks like it's a Node.js object.
I know there's also another method to include the dist version of the plugin. I also tried it, but I got some errors: 'Identifier '_classCallCheck' has already been declared'. I looked deeper and I found that actually many of those files are declaring the '_classCallCheck' function and of course that it a redeclare error. Maybe I can refer to this particular error directly to them (MDBootstrap).
So here I am, trying to include and use these modules/files in my Laravel project without success. Any help is appreciated.

NodeJS - how can I make my code beautiful / cleaner / better readable

I have generated my app with express --view=pug myapp which created me a folder-tree with the files I need to start over.. I wrote some code which I would like to outsource from the main app.js in maybe a function-file or something like that, to keep the app.js cleaner.
where would I put my custom functions? how would I then require the function-file in nodeJS ?
You can arrange your files as you wish. Wherever you keep your functions, just add the functions you want to use in any other files to module.exports object in that file. Then in your app.js (or any other file where you want to use these functions), import the file using require and you should have access to all the exported properties and functions from the file you import.
For example:
I can put my functions in ./lib/core-lib.js:
function test(){
// do something
}
module.exports = {
test: test
};
And then in my app.js
const lib = require('./lib/core-lib');
lib.test();

Call js function defined in a babel script from HTML

I am including my js file into my main html file like so
<script type="text/babel" src="js/scripts.js"></script>
Then I call one of my functions like so
<div class="allButton" id="completeAll" onclick="showAll('completeColumn');">Show All (...)</div>
the function looks like this
function showAll(column) {
$('div[id^='+column+']').removeClass('hide');
};
When I click the button(div) I get this error
Uncaught ReferenceError: showAll is not defined
I am using the text/babel as my script type because the file contains React JS stuff.
I have no idea why I simply cannot call my function. I am extremely new to ReactJS and Babel. (note: I am not using npm/gulp due to limitations)
Any help and/or advice would be appreciated
If you just define your function as follows you will be able to call it within the HTML.
window.showAll = function showAll(column) {
// your code here...
};
You have not exported your showAll function. When you transpile a JS/JSX file with Babel and bundle it to a scripts.js file (using Browserify or similar utilities), you must make sure to export your module (which tells your bundler to package it into your bundled file).
Your code should look like this:
var showAll = function(column) {
$('div[id^='+column+']').removeClass('hide');
};
module.exports = showAll;
This tells your bundler that your showAll method needs to be exported and available to other referenced namespaces.

Categories