I am new to Rails and I have a pretty simple problem with calling javascript functions from within a view. In Rails 2 I would do...
= javascript_tag "name(arguments)"
where the javascript function "name" was located in my application.js file. However, this does not appear to work in Rails 3? Or am I missing something? I have been searching Google for some time without finding an answer.
UPDATE:
OK, so I looked at the source of the two different ways (using the javascript_tag and the haml javascript filter) as suggested. And this is very strange because the html source appears to be identical? Apart from a difference in double and single quotes in declaring the script type.
FIRST: using the javascript_tag which does not work
= javascript_tag "number_interval(#{fact.current_value}, #{fact.growth_per_second}, #{fact.decimal_number}, '#{dom_id(fact, "number")}'"
Source...
<div id='number_number_interval_727'>loading</div>
<script type="text/javascript">
//<![CDATA[
number_interval(6952596670.36814, 2.33002440293917, 0, 'number_number_interval_727'
//]]>
</script>
SECOND: using the haml javascript filter and it works
:javascript
number_interval(#{fact.current_value}, #{fact.growth_per_second}, #{fact.decimal_number}, '#{dom_id(fact, "number")}')
Source...
<div id='number_number_interval_727'>loading</div>
<script type='text/javascript'>
//<![CDATA[
number_interval(6952596917.02179, 2.33002440293917, 0, 'number_number_interval_727')
//]]>
</script>
Well, I guess I'll just stick with the haml filter!
You have a syntax error:
= javascript_tag "number_interval(#{fact.current_value}, #{fact.growth_per_second}, #{fact.decimal_number}, '#{dom_id(fact, "number")}'"
is missing the closing parenthesis for the number_interval function. I think that it should be:
= javascript_tag "number_interval(#{fact.current_value}, #{fact.growth_per_second}, #{fact.decimal_number}, '#{dom_id(fact, "number")}')"
A friend of mine pointed me to the fact that there is a javascript helper in haml. Apparently, I can call javascript functions by using...
:javascript
name_of_function(arguments)
This works, but of course only with haml.
Check out the right syntax on link text
and Check if you have included the javascript defaults in your file.As this working for me in RAILS 3 also
Silly question but... is application.js included in your layout?
One option to try is to hand-code the js function-call into your view in "real" javascript - just to see if it works. eg
<script type="text/javascript">
name(arguments)
</script>
Then you can be sure it's not the js itself (or lack thereof) at fault.
Related
For example, I have a page /locations/map which I need to include Google Map library, and include a .js file (e.g. location.js) specifically for this page only.
I want to inject these 2 files to after <!--SCRIPTS END--> this line
Is it possible to do this?
NOTE: I was using Sails.js v0.10
Sails uses ejs-locals in its view rendering, so you can accomplish what you want with blocks.
In your layout.ejs file, underneath the <!--SCRIPTS END-->, add (for example):
<%- blocks.localScripts %>
Then in the view you're serving at /locations/map, call the block with your script tag, for example:
<% block('localScripts', '<script src="https://maps.googleapis.com/maps/api/js"></script>') %>
As an alternative, you could put the <!--SCRIPTS--> and <!--SCRIPTS END--> tags in the <head> of your layout, and then add your view-specific scripts directly into your view rather than using blocks. This is a fine option if you don't mind waiting for those linked scripts to load before your page content is displayed.
Scott's answer is the proper way to insert non-global JS into a specific view. Just a little comment, though: the block call from the view should not have the dash. It should be as follows:
<% block('localScripts', '<script src="https://maps.googleapis.com/maps/api/js"></script>') %>
Both calls will work, but using the dash makes the insertion twice; once the view is loaded and previous to the layout render, and then once again when the view is inserted in the rendered base layout. This leads not only to inserting/running unnecessarily twice the same code but also to errors that break your JS code if the inserted script depends on libraries that you have in your base layout (e.g. jQuery, Backbone).
EJS interprets the magic <%- as "insert unescaped". So, -I guess- what this is doing is calling the block() function, which returns our HTML <script> tag. This is replaced where the magic was called but also is executing the block() function inside of it, which is executing the layout block localScripts replacement.
On the other hand, <% means "instruction". I.e., just run this JS piece of code, which is not echoed to the view where is called.
I discover other way to do that
In MapController.js
// you can add as many as you like
res.locals.scripts = [
'//maps.googleapis.com/maps/api/js',
];
return res.view();
In layout.ejs
<!--SCRIPTS-->
<!--SCRIPTS END-->
<!-- Loop through all scripts that passed from controller -->
<% if (scripts) { %>
<% for (i = 0; i < scripts.length; i++) { %>
<script src="<%- scripts[i] %>"></script>
<% } %>
<% } %>
This method allows flexibility to locally serve js files from any page and also prevent any reference errors caused by dependencies.
In pipeline.js insert '!js/local/*.js at the bottom of jsFilesToInject like so:
var jsFilesToInject = [
// Load sails.io before everything else
'js/dependencies/sails.io.js',
// Dependencies like jQuery, or Angular are brought in here
'js/dependencies/jquery-3.3.1.min.js',
'js/dependencies/**/*.js',
// All of the rest of your client-side js files
// will be injected here in no particular order.
'js/**/*.js',
//Ignore local injected scripts
'!js/local/*.js'
];
Create a local folder inside the /assets/js folder ie /assets/js/local/. Place any locally injected scripts in here.
In your master view ejs ie layout.ejs insert <%- blocks.localScripts %> below the SCRIPTS block like this:
<!--SCRIPTS-->
<script src="/js/dependencies/sails.io.js"></script>
<script src="/js/dependencies/jquery-3.3.1.min.js"></script>
<script src="/js/dependencies/bootstrap.min.js"></script>
<script src="/js/dependencies/popper.min.js"></script>
<!--SCRIPTS END-->
<%- blocks.localScripts %>
In your local ejs view (eg. homepage.ejs) insert your localScripts block like this:
<% block('localScripts', '<script src="/js/local/homepage.js"></script>') %>
sails v0.12.14
EDIT
Is this still relevant for Sails v1.0?
My answer is a resounding YES and in my earlier answer I lacked explaining how to get the most out of the Grunt pipeline like clean, coffee, concat, uglify etc... when going into production.
The trick here is to make a local file (there should only be one per page) as small as possible.
Group and name specific your function calls
Save functions as separate files for easy maintenance and group them into folders.
Group bindings and any initialising of global variables into a couple of functions like initThisPageVariables() and initThisPageBindings() so that Grunt can crunch these later.
Set a master function call to run your app startThisPageApp()
Then simply calling the few functions from your local (master) file to get things rolling.
$(window).on('load', function(){
initThisPageVariables();
initThisPageBindings();
$(window).on("resize", function(){
initThisPageVariables();
}).resize();
startThisPageApp();
});
I know this is an old question by I discovered another option.
File:
config\routes.js
Code:
'/': 'HomeController.StartHome'
I set StartHome function in HomeController responsible for managing '/' route.
File:
api\controllers\HomeController.js
Code:
StartHome: function(req, res) {
var template_data = {
"data" : {
"view_name" : "home_view"
}
}
res.view('home_view', template_data)
}
Here, I created an object with some data which is passed to EJS template(to client).
File:
views\layout.ejs
Code:
<% if (data["view_name"] === "home_view") { %>
<script src="script_for_home_view.js"></script>
<% } %>
In my layouts.ejs I created if statement which "enables" script tag depending on on the view I am currently on.
This is how I handle this. I hope it's clear for you.
Sails 0.12.4
I'm using HTML, JavaScript, and jQuery Mobile to make a kind of picture gallery. I'm following the JQM demo at: http://jquerymobile.com/demos/1.3.0-beta.1/docs/demos/swipe/swipe-page.html
to make the gallery, but it uses a totally different HTML page for each picture. My plan is for the gallery to be dynamic, so I don't have a set number of pages or a set list of picture names, etc, and I thought I might use Mustache to make a picture template, and create the pages dynamically. Here is the basic layout of the code:
In index.html
<!DOCTYPE html>
<html>
<head>
...
<script src="mustache-0.7.0-min.js" type="text/javascript"></script>
<script src="mobile.js" type="text/javascript"></script>
<script id="test_template" type="text/html">
<h1>{{firstName}} {{lastName}}</h1>
<p>{{tempText}}</p>
</script>
...
</head>
...
And then in mobile.js
function showPerson()
{
var person =
{
firstName: "Feaf",
lastName: "McFeaf",
tempText: "Hello Feaf"
};
var personTemplate = document.getElementById("test_template").innerHTML;
var html = Mustache.to_html(x, person);
}
So it's about as basic as you can get. However, when I run the web app on a local server (in Chrome), and I step through this function, I get an error at the Mustache.to_html line, saying
Uncaught TypeError: Cannot call method 'to_html' of undefined
I'm fairly new to web development, and brand new to Mustache, so I do not know what could be causing this error. I've tried calling other Mustache methods, like render, but the same error appears. Is the <script src=...> not enough to have the Mustache library accessible to mobile.js? Anybody have any tips on what I might be doing wrong?
Thank you for any information, and let me know of any other information I should add.
EDIT:
Whoops! Forgot to include the fact that I had mustache in the scripts section, I've edited to reflect this fact. Just to be clear, I DO have (and always have had) mustache included!
Also, I tried the suggestion of #Zorayr of using console.log(Mustache), and it claims that Mustache is undefined, even though I am importing it as noted above. Why might this be?
As a solution to the problem, I ended up downloading and using Handlebars. It seems to me that there was some conflict with the Mustache library that was already in the project and the new one I added in. This doesn't really explain why Mustache would be undefined, but that was my workaround.
Today when I was working on some javascript in an Asp.NET page, I did something like this:
<script type="type/javascript">
var elementID = document.getElementById("<%=txtMyServerControl.ClientID %>")
</script>
And compiler threw an error and once I added a space between = and txtMyServerControl and it stopped complaining about it and worked. BUT however without space it is working perfectly on my co-worker's computer?
Am I missing something?
Note: This is the error, there wasn't any other problem and yes, I compiled several times before realizing this was the error.
<script type="text/javascript">
var elementID = document.getElementById("<%=txtMyServerControl.ClientID %>")
</script>
I have in my application layout file an external javascript file witch has several lines of code and at the end runs a function like BooManager.init() no big deal...
the problem is, it is not running the inside code on this javascript file.
this is how i use it:
<script type="text/javascript">
bb_bid = "1615455";
bb_lang = "en-US";
bb_keywords = "iphone4s, apple";
bb_name = "custom";
bb_limit = "8";
bb_format = "bbb";
</script>
<%= javascript_include_tag "http://widgets.boo-box.com/javascripts/embed.js" %>
but it didn`t do anything it was suposed to do...
i`ve tried in simple html file and it works... what am i doing wrong?
NOTE:
the default way in html is this:
<script type="text/javascript">
bb_bid = "1615455";
bb_lang = "en-US";
bb_keywords = "keywords, between, commas";
bb_name = "custom";
bb_limit = "8";
bb_format = "bbb";
</script>
<script type="text/javascript" src="http://static.boo-box.com/javascripts/embed.js"></script>
-- EDIT --
the result generated by rails:
<script type="text/javascript" src="http://static.boo-box.com/javascripts/embed.js"></script>
It's not evaluating the script when loading using the <%= method. I'm not familiar with that syntax, but from the effect, that's what it sounds like. It's treating the script as html rather than code.
jQuery has a script load function that will get a script dynamically from a URL and then eval() it to execute it.
UPDATED WITH SAMPLE CODE
Add jQuery to your app:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
Then use it to load your script:
$.getScript('http://widgets.boo-box.com/javascripts/embed.js');
UPDATE NUMBER 2
I was able to duplicate the problem in this fiddle: http://jsfiddle.net/7x2zT/4/
If what you are trying to accomplish is to get your parameters activated before the script shows the widget - the default one looks like a sidebar, whereas your parameters make it more of a banner, then just make sure you put your parameters above the <script src stuff.
If you must be able to load dynamically, then you're going to have to figure out where the bug lies in the embed code, or if there's some other activation method. That site's documentation doesn't seem to be in English, so I can't help with that.
I am trying to use an Apycom menu requiring jQuery 1.3.2 on the same pages as Flexigrid for Rails which depends on jQuery 1.2.3. To add to the confusion I'm trying to include the Rails 'prototype.js' and use this as well. Here is my include order:
<%= javascript_include_tag :defaults %>
<%= yield(:head) %>
<script src="/javascripts/jquery.js" type="text/javascript"></script>
<script src="/javascripts/flexigrid.js" type="text/javascript"></script>
<script type="text/javascript">
jq123 = jQuery.noConflict(true)
</script>
<script src="/javascripts/menu/jquery.js" type="text/javascript"></script>
<script src="/javascripts/menu/menu.js" type="text/javascript"></script>
<script type="text/javascript">
jq132 = jQuery.noConflict(true)
</script>
When the page I'm testing loads up, Firebug gives me the following:
$ is undefined
(240 out of range 237) menu.js (line 240)
and consequently my menus don't work (at least not the parts that matter). I don't have a Flexigrid grid on this page so I can't attest to whether that even works. I saw this answer (How do I run different versions of jQuery on the same page?) but it doesn't completely work. My local JavaScript works but the jQuery plugins don't seem to be happy.
Any suggestions?
Menu.js is assuming the jQuery function is called $. When using jQuery.noConflict you need to update the references to the jQuery function to whatever you named it (in this case jq123 or jq321).
#MightyE makes a good point that menu.js file is referencing the $ object incorrectly. An easy solution to fix this would be to open you menu.js file and wrap the contents in this function call
(function($) {
// $ is now equal to jq321
// menu.js stuff
})(jq321);