How to call a javascript function inside a rails view? - ruby-on-rails

I did just a upgrade from RAILS 5 to RAILS 6 and I see that all rails views are not able to call a javascript function as before in RAILS 5.
I have an external javascript file located under
app/javascript/packs/station.js
This is is embeded in in app/views/layouts/application.html.erb as
<%= javascript_pack_tag 'station' %>
This is the code how I call the javascrpt function from html.erb file :
<%= text_field_tag(:station_text_field, ... ,
onkeyup: "javascript: request_stations(); ") %>
When I try to call a function thats is part of the station.js then I get an error in the browser developmer view: ReferenceError: request_stations is not defined
But I can also see in the brwoser view, under Debugger :
Webpack / app/javascript / packs / station.js
and the javascript function I want to call.
So it seems that this script was loaded by the browser.
In contrast, when I just copy and paste these few lines that represent this javascript function direct into the template view file (...html.erb), something like :
<script>
function request_stations ()
{
alert("calling request_stations");
};
</script>
then - it works as expected !

By default, variables/functions defined inside JavaScript files that are packed by Webpacker will not be available globally.
This is a good thing, because it prevents global naming conflicts. Generally speaking, you don't want to reference javascript functions/variables from your view. You instead want to write JavaScript in a way that attaches functionality to DOM nodes using their id or other attributes.
Here is a basic example based on the code you provided:
# in your rails view
<%= text_field_tag(:station_text_field, ..., id: 'station-text-field') %>
// in your javascript
function request_stations() {
alert("calling request_stations");
};
const stationTextField = document.querySelector("#station-text-field");
stationTextField.addEventListener('keyup', (event) => {
request_stations();
});

Agree with mhunter's answer.
This post helped me get a grounding on this difference in Rails 6: https://blog.capsens.eu/how-to-write-javascript-in-rails-6-webpacker-yarn-and-sprockets-cdf990387463
What I don't see in your question is whether or not you did this in app/javascript/packs/application.js:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("station")
The big difference in Rails 6 is that you have to deliberately:
require a JS file
deliberately export something from that file
deliberately import that something, in the file where you want to use it.
So if there is a function in station.js that you want to use, connect the steps above. Start with a simple function in station.js that fires upon DOMContentLoaded, and add a console.log("hey, station.js is alive and well"). If you don't see it, then something in those 3 steps is not right.
In pre-Rails6, you had a "garden" of JavaScript, just by virtue of being in the asset pipeline. In Rails 6, you have to be more deliberate.

Related

How to access custom javascript functions via browser-console in Rails 6

For the sake of debugging the javascript-part of a Rails 6 (version 6.0.0.rc1) web application I want to use my custom javascript functions also in the Chrome console (aka. Inspect).
Back when I used just static HTML files to build a website (as opposed to using a web-framework like Rails as of right now) you would simply embed the JS file in the DOM like this
<!-- custom JS -->
<script src="js/custom.js"></script>
and could instantly access and execute all custom functions that were placed in this file.
Background:
The JS file is placed at the correct rails 6 specific directory as provided in this article: How to require custom JS files in Rails 6
Note:
The rails 6 application also uses the JS file already, since the browser shows the console log message.
Here is the full content of the JS file:
// app/javascript/packs/custom.js
console.log("loaded custom.js successfully")
function sayHello () {
console.log("Hello good Sir or Madam!")
}
Expectation: I am expecting to open the browser's (Chrome) console and be able to use the sayHello() function in the console.
However, when I do so, I get an error message in the console stating:
Uncaught ReferenceError: sayHello is not defined
Try something like
sayHello = ()=>{
console.log("Hello good Sir or Madam!");
}
then you can evoke in console:
>sayHello();

How to use rails webpack loaded js in js erb template

I am using webpacker gem in rails 5.0 app, but I can't get the javascript to execute/be available in the js.erb I'm hitting on validation error. I'm sure that I'm violating some simple premise here, but can't find the answer and no compile or console errors are present. I do have the <%= javascript_pack_tag 'application' %> in my application.html.erb
Here is the setup:
app/javascript/packs/application.js:
import * as CustomerSession from 'customer_sessions';
console.log('Hello World from Webpacker');
app/javascript/customer_sessions/index.js:
export { univgwTabs } from './univgwTabs';
app/javascript/customer_sessions/univgwTabs:
export let univgwTabs = () => {
console.log('hi');
};
js erb template called on validation error of form:
$("#right_side_right_bottom_target").html("<%= j render partial: 'generic_object_new' %>");
CustomerSession.univgwTabs();
Global.resizeWindow();
If there is no issue with console.log(CustomerSession) in the location where you are importing it then the simplest fix I found was to just assign the imported variable as one of the global windows properties.
// in packs/application.js (or wherever your pack is)
// import
import * as CustomerSession from 'customer_sessions';
// assign
window.CustomerSession = CustomerSession
Assuming that there is no issues with webpacker, you should now be able to console.log(CustomerSession) in the console or your js.erb file.

Uncompiled partials in dust.js

I'm trying to figure out how to include an uncompiled dust partial in a dust view.
main.dust:
<div class="container">
{>"toolbar"/}
</div>
toolbar.dust:
<div class="row">
toolbar
{somevariable}
</div>
This attempts to load a compiled partial named toolbar.js not the uncompiled toolbar.dust template?
I found this post on here: How do you include a raw, uncompiled partial in dust.js? which will let me load files but any {variables} they contain are not replaced.
I don't want to have to compile views every time I change them. How can I include an uncompiled template with its variables replaced?
You can add an onLoad handler to Dust to tell it how it should try to load partials. By default, Dust expects you to have pre-registered all templates you're going to try to use.
More info: Loading Templates (it sounds like you want to load Uncompiled Templates)
Note: you shouldn't really do this in production, because compiling templates is much slower than rendering them. If you're using something like Express, take time to set up a build step or an Express plugin to compile for you. There are examples in the examples directory on the Dust GitHub repository.
An onLoad handler might look something like this (assuming you're running Dust on the server-- the idea is the same for the client):
dust.onLoad = function(templateName, callback) {
fs.readFile(templateName + '.dust', { encoding: 'utf8' }, function(err, data) {
callback(null, data);
});
};
Notice that callback uses the Node errback signature, so you can simplify this code to:
dust.onLoad = function(templateName, callback) {
fs.readFile(templateName + '.dust', { encoding: 'utf8' }, callback);
};
There is an example on our GitHub repo that does basically this.

jasmine not loading my custom js file

I am unable to get jasmine to load my custom javascript file, even though it works perfectly in the browser. I've reduced the javascript to the minimum to avoid any possibility of errors and I still get a failing test on the simplest thing.
Custom ARB.js file:
$(document).ready(function() {
alert('typeof ARB: ' + typeof ARB);
});
ARB = {};
ARB.VERSION = "V1.01.00 2012-08-24";
jasmine configuration file snippet (I'm on Rails 3.0.9):
src_files:
- "public/javascripts/**/*.js"
- "public/javascripts/ARB.js"
This test:
it('should define the custom javascript functions', function() {
expect(typeof ARB).toEqual('object');
});
fails with:
Expected 'undefined' to equal 'object'.
jQuery gets loaded and so does my application.js file. When running jasmine, I get no alert message, but I do when I run my app.
What am I missing?
UPDATE: If I remove the $(document).ready function, jasmine passes all the tests - what's that all about?
shioyama gave me the pointer that I needed to figure this out: my custom ARB.js file was getting loaded before the jquery files so it didn't have access to the $(document).ready function. What I had to do was explicitly spell out the order in which my javascript files were to be loaded. Here's what I put in my jasmine config file at /spec/javascripts/support/jasmine.yml:
src_files:
- "public/javascripts/**/jquery.js"
- "public/javascripts/**/jq*.js"
- "public/javascripts/**/application.js"
- "app/assets/javascripts/**/*.js"
- "public/javascripts/ARB.js"
I first force the main jquery.js file to load, then all the other jquery files, then application.js, then any other javascript files that are located in the assets directory, and finally my custom javascript file.
This works for me because I'm still on Rails 3.0.9 and starting the migration to 3.1+ and the asset pipeline.

Using jquery-ui in ClojureScript with jayq

I'm trying to use jayq with jquery.ui.sortable to make a list on a page
sortable. Looking at http://jqueryui.com/demos/sortable/ it seems like it should be as
simple as:
(.sortable ($ :#sortable))
Which compiles down to:
jayq.core.$.call(null, "\ufdd0'#sortable").sortable();
And throws:
Uncaught TypeError: Cannot call method 'call' of undefined
when I try to include it in a page. Interestingly, the generated code
does work in the page when I paste it into the js console, which implies
to me that something necessary is loaded after that line is executed.
I've modified
(def cljs-options {:advanced {:externs ["externs/jquery.js"]}})
to
(def cljs-options {:advanced {:externs ["externs/jquery.js" "js/ui/jquery-ui.js]}})
after reading
http://lukevanderhart.com/2011/09/30/using-javascript-and-clojurescript.html
, which doesn't seem to be sufficient. I'm guessing that jquery.ui
modifies the $ Prototype, but I'm not sure how to accomplish this in
clojurescript
I'm also using noir and noir-cljs, if it makes any difference.
Looking at using jQueryUI with closure compiler it might just be that jquery-ui needs a hand rolled externs file in order to be used, possibly a major undertaking. Can anyone confirm?
There were two separate aspects to solving this.
To compile in advanced mode, I needed to add the following to my externs file.
$.prototype.sortable = function (a,b) { };
$.prototype.disableSelection = function (a,b) { };
I'm using noir-cljs, and in my view template, had the following:
(:require [noir.cljs.core :as cljs])
(:use [hiccup.page :only [include-js]])
...
(cljs/include-scripts :with-jquery)
(include-js "/js/jquery-ui.js")
But this can't ever work, since the jquery-ui code needs to be included after jquery but before the generated ClojureScript. The solution is to manually include the libraries in the page:
(include-js "/js/jquery.js")
(include-js "/js/jquery-ui.js")
(include-js "/cljs/bootstrap.js") ;; Generated

Resources