Rails Javascript - Calling a javascript function from the view - javascript

I have a javascript file abc.js. I have kept it inside app/assets/javascripts/abc folder.
In my application.js file, I have given
//= require abc/abc
In my view, I need to display the object that is returned by a function inside the abc.js file.
I am calling the function by the following command
%h4.sub-header Test Project
%textarea.form-control{rows: 10}
:javascript
abc.diff(#a, #b)
I am getting the a & b values from the controller.
But the function is not being called. Instead the sentence "abc.diff(a,b)" is being displayed.
Could anyone tell me how to call the function inside my view?

Inside haml javascript blocks you can just do string interpolation. Also indentation is very important in haml (obviously). So your view should look like:
%h4.sub-header Test Project
%textarea.form-control{rows: 10}
:javascript
abc.diff(#{#a}, #{#b});
Without more info it is hard to say more, but in general this is not the preferred way: your code should be precompiled in javascript assets, and generally I define variable or add data attributes to specific items.
So either something like
:javascript
var a = #{#a};
var b = #{#b};
ad inside abs.js.coffee write
$ ->
abc.diff(a,b)
The cleanest solution imho is something like:
%body{'data-a': #a, 'data-b': #b}
and inside abs.js.coffee write
$ ->
a = $('body').data('a')
b = $('body'_.data('b')
abs.diff(a,b)
Of course, this depends whether it applies to your solution, my apologies for digressing :)

Related

Using Ruby Variables in JavaScript

I'm new to Ruby so I had a question in regards to using some variables throughout my codebase. I have a variable declared in my Ruby file as follows:
#pages = 350
Now, I know that in HAML, I can simply do:
-if #pages = 350, and in HAML with inline javascript, I can do something like:
:javascript
console.log("#{#pages}");
However, I am linking an external JavaScript file in my HAML document, and I was wondering if it would be possible to use my Ruby variables in my external JS document? I need some variables in order to build what I am trying to build. Thanks!
Update as per one of the answers:
In my HAML file, I have the following:
:javascript
printPages(3);
In my external JS file, I have:
$(function() {
window.printPages = function(pages) {
console.log(pages);
}
});
I you are loading static javascript files I really would not recommend trying to insert variables into that code.
Instead, think about how you would provide that to your javascript code.
You could send it to that code as an argument.
<script type="text/javascript" src="./my-external-script.js"></script>
:javascript
runMyExternalFunction(#{#pages})
Or you could add the variable as a data attribute on your page, and your javascript could look for that when it loads.
Haml:
%body data-pages=#pages
JS:
console.log(document.body.dataset.pages) // value of #pages
Update about your javascript:
Using jquery's on page ready event $() for declaring the function is a bad idea. That means that it waits for everything on the page to be compeletely loaded, and then declares the function your page needs.
What you should be doing is setting up all the function that will be needed right away, and then calling those functions when the page is loaded.
They way you have it, the page loads, printPages() is executed (which doesn't exist yet), and then after all that printPages is created and put into the global scope.
So remove the $() from your external script and add it to your HAML instead.
In fact, if your aren't doing anything super fancy with transpiling or bundling, then you can simply declare the function in your external JS file, and it will be available globally. This doesn't work with your code because they way you've declared the function, it would only be available to code within that $() callback.
// js file
function printPages(pages) {
console.log(pages);
}
HAML:
$(() => {
printPages({#pages})
})

<%= %> syntax in Typescript/Angular

I'm working on an Angular project that makes use of a variable called WEB_HOST. I can see in another file where the variable is defined (it's somehow defined in a class called ProjectConfig).
Definition (simplified):
export class ProjectConfig {
WEB_HOST = 'https://example.com'; // I didn't think properties could be assigned here?
constructor() { ... }
}
Now, from a completely separate *.ts file, this obviously doesn't work:
let x = WEB_HOST; // x is undefined
However, in the same file, some existing code manages to access the variable's value by using a bizarre string interpolation:
let x = `<%= WEB_HOST %>`; // x == 'https://example.com'
What is going on here? I've never seen this <%= syntax before. Is it part of Angular? Typescript? I haven't been able to find any documentation on what it does.
I also don't know how it manages to get a property out of ProjectConfig (and I'm not sure it's a class property either... the definition being outside of any class function confuses me too).
So, this is ASP Tags.
If you take a look at the following from w3schools. https://www.w3schools.com/asp/showasp.asp?filename=demo_syntax_tags
You will see that it is used in the HTML for templating. It can also be used I believe in other files as it is processed on the server before the client application runs on your browser.
HTH
Looks like ERB, a Ruby templating engine that Ruby on Rails uses.
https://codingbee.net/tutorials/ruby/ruby-the-erb-templating-system
Others have mentioned that various languages use <%= some_code_here %> for templating.
Regardless of what language is processing it, I imagine your .ts file runs through some server side framework, that will put some string value in place of WEB_HOST, and then the .ts file will be transpiled to JS.
You could try this way on HTML file
{{window.location.origin}}
if it doesn't recognize the window variable we might need this in ts file
declare const window;

How to Add Global/Public Variables to grunt-contrib-uglify Output

Okay, so I am way new to Grunt and Node.js. I am building a site, and decided that the 'main.js' file was getting way too big. So, I split it up, and I am now trying to use Grunt to piece all of these JS files back together.
The issue that I have is that I need to make some global variables available to all of the various functions in all of these JS files. To be more specific, every page on our site is identified via an id in the body tag:
<body id="home">
Many of these JS files contain if statements that ensure certain functions only run if the appropriate page is loaded. For example:
if (page == 'home') {
var title = "Home Page"
$('.page-title').text(title);
}
Notice the page variable? That guy is the one that I need to make available to all of these files (after grunt-contrib-uglify merges them together). So, I figured I'd assign a new "unique" variable name, and make it global.
I noticed that grunt-contrib-uglify has a 'wrap' option listed in its documentation. However, no examples are given as to how to use it.
Can anyone tell me:
- How to use the 'wrap' option in 'grunt-contrib-uglify'
- If this is the right grunt plugin for what I am trying to do?
One idea I had (as a last resort) is to create a before.js and after.js and put the beginning and end (respectively) of what I wish to wrap around the other files in each. But, I think the 'wrap' option is what I need, yes?
UPDATE: Here is a link to my "merged" JS file:
main.js
And a link to my Gruntfile:
Gruntfile.js
I have been having the same problem an searching for a solution. But I think I found an answer.
Use this in your gruntfile:
uglify: {
options: {
wrap: true
}
}
The documentation for the wrap property indicates that the variables will be made available in a global variable, and looking at the generated code that does seem to to be the case. Passing a string value to the parameter does seem to create a global variable with that name.
However, wrap: true seems to make all objects and properties available in the global scope. So instead of globals.page.title (which I can't get to work, anyway), you can just use page.title. Much, much easier and simpler.
If this suits your purposes, I'd recommend doing this instead.
Ok this one is tricky, I have been stucked for a while...
Way you do this with grunt-contrib-uglify for frontend JS
create multiple files like
SomeClass.js
OtherClass.js
main.js
and use some module (grunt-file-dependencies or grunt-contrib-concat) and setup it to concat your files. Then setup uglify in your Gruntfile.js like
...
uglify: {
options: {
wrap: "myPublicObject",
...
},
In file (main.js for example) exports variable has to be assigned, the entire file might look like this:
var otherClassInst = new OtherClass();
var someClassInst = new SomeClass();
exports = otherClassInst;
Now what it exactly does
Uglify will pick superior context (this) and define property on it named myPublicObject. Then it wrap your sourcecode with function and create exports variable here (DO NOT DECLARE var exports anywhere). At the end it will assign what you exported to that property. If you dont assign anything (you dont use exports =) inital value is {}, so the property with void object exists anyway.
To make this super-clear,
if you put your code into page like <script src="myminifiedfile.min.js"></script>, then superior context is window =>
window.myPublicObject is instance of OtherClass while window.someClassInst, window.SomeClass and window.OtherClass are undefined.
this is unlikely, but if you just copy content of minified result and wrap it with different function, object you exported will be visible only via this["myPublicObject"] => uglify wrap doesn't make things globaly accessible, it makes them accessible in superior context.

How to get rails controller variable in js file

I have a variable in rails controller like
def index
#approveflag = true
end
I need to access this variable in my javascript code, I used the code given below
in index.html.erb
<script>
alert("<%=#approveflag%>")
<script>
It works fine with result "true".But when I moved this code to its corresponding .js file it gives an alert with result string ""<%=#approveflag%>"".
What is the reason. How can I solve this?
Thanks in advance.
I personally don't like mixing js with erb so I would do something like this:
in index.html.erb
<%= content_tag :div, '', id: 'mycontainer', data: { source: #approveflag } %>
in js
$('#mycontainer').data('source')
alert($('#mycontainer').data('source'))
And you have the value from that instance variable, you can add a data attribute in a link_to if you want.
You cannot use the js files for this, they are part of the asset pipeline, they are compiled/compressed, and preferably only downloaded once by your client (browser). So they should not change. So at the time the js-files are precompiled, the instance variable is not set and would not make any sense anyway.
But there are a few options.
You can declare a javascript variable in your view, which your javascript can use (for global data)
Code (I use haml):
:javascript
var approveFlag = #{#approveflag} ;
You can declare data-tags on elements, if the data belongs to a specific element. But for instance, you could also a data-tag on body element
For instance
%body{:'data-approveflag' => #approveflag}
Or something like
= link_to 'Collapse', '#', 'data-collapse-div' => 'class-to-collapse'
Alternatively you can use ajax/json to download the data. This is clean, but adds an extra delay, so only do this is if the data is not required immediately.
I recommend that you to use the 'gon' gem
I'm usually more oriented to straight options that don't imply installing, but trust me using 'gon' is very direct and clean
In your Gemfile add this lines
# To use controller variables as javascript variables
gem 'gon'
Do a bundle install to get/install the gem
In your app/views/layouts/application.html.erb add the following line
<%= include_gon %>
Then in your controller pass your variable to gon as simple as this
gon.your_variable = #controller_variable
And in your javascripts retrieve your variable (in this case to show an alert)
alert (gon.your_variable) ;
And That's it !
For more examples and details see the Wiki page - https://github.com/gazay/gon/wiki
In my project I need sometimes user language related translations. So what I did is, I created a simple js file to store the localized text:
var TRANSLATION_ARRAY = []
function store_translation(key, value) {
TRANSLATION_ARRAY[key] = value
}
function get_translation(key) {
return TRANSLATION_ARRAY[key]
}
In den index.html.erb or other components I do this to store the localized text:
store_translation('text_id', "<%= translate_with_user_language('text_id') %>")
In my plain js file located in app/assets/javascripts i get the localized text by:
get_translation('text_id')
Works very well. You can use this approach also for other purposes not only translations.

Give preference to JS file function over HTML file

I have two functions of the same name; let's say foo().
One in the HTML file and one in the JS file, which is included in the HTML file. The problem is I want to give preference to the JS file function rather than the HTML file function.
Is there any way to do that, or is there any syntax in JavaScript like [JSFileName].foo(), that may perhaps call the function in the JS file?
Not sure why you want to have two identically-named functions.
The snarky answer is: Just remove the reference to the function you don't want. (If you have control over your html, such a situation shouldn't exist.)
The answer you're looking for: Place the external script tag after the inline script tag.
Make sure the script tag for the js file is after the HTML script tag in which foo is declared.
It's not clear from your question why you have two functions named foo, but based on your [JSFileName].foo() attempt at a solution, I might suggest using objects as namespaces. In your script you could do:
var myScriptFunctions = {
foo: function() {
// do foo stuff
}
}
You can call it with myScriptFunctions.foo() and you won't have two functions competing for the global name foo.

Categories

Resources