I have a rails application where I am trying to have jQuery render WITH an HTML file. The point of this is that the jQuery is specific to the page so I don't want it loading for every page by putting it in the header. Here's what I have done to the files
messages_controller.rb
# GET /messages/new
def new
#message = Message.new
#users = User.where('id != ' + current_user.id.to_s)
#if #message.save
#MessageMailer.new_message(#message).deliver
respond_to do |format|
format.html # new.html.erb
format.js
end
#end
end
/messages/new.html.erb
<div class="row">
<div class="large-12 columns">
<div class="panel">
<h1>New message</h1>
<%= render 'form' %>
<%= link_to 'Back', messages_path, :class => "button small" %>
</div>
</div>
</div>
/messages/new.js.erb
$("h1").html('hello'); //for testing that it works
Is there something that I have to add to another file? Is there a request I have to make so it loads both the HTML and .js?
Why this won't work?
I don't think your method is a particularly standard way to accomplish what you want. The reason why your JS template is not being loaded is because your controller is setup to respond with an HTML template, if the browser requests HTML, and a JS template, if the browser requests JS. It will never return both because the browser can only request one or the other.
In other words, the respond_to block in your controller is instructing the controller how to respond to requests for different types of content, not listing all of the types of content that will be sent, on every request.
Solution 1: Load the JS on every page
There are a couple of things to consider here. Firstly, just because JS is not used on a particular page, is not, in itself, a good reason not to load it. Rails, via Sprockets, will concatenate and minify all of your JS into a single file. As long as this file does not change, then it will be cached by a user's browser. This will result in a faster response when your user visits a page that does require this JS, as it won't need to make an extra request in order to get the new JS.
If you are concerned about your JS running on content that it shouldn't, then you should really be guarding against this by adding a unique identifier to the page, or pages, on which you want it to run, then detecting that identifier in your JS. For example:
$("h1.special").html('hello');
Solution 2: Use content_for
However, there are some circumstances in which it might be beneficial to load JS separately. The best way to accomplish that, in my opinion, is to have a content for block that appends content to your head, if an individual view needs it.
So in your application layout:
#layouts/application.html.erb
<head>
#Other head content here
<% if content_for?(:head) %>
<%= yield(:head) %>
<% end %>
#Other head content here
</head>
#messages/new.html.erb
<% content_for :head do $>
<%= javascript_include_tag "messages_new.js" %>
<% end %>
#Other view stuff here
#assets/messages_new.js
$("h1").html('hello');
messages_new.js will then only included, and therefore run, on views where you include that content_for block. It should not appear on any other page.
Solution 3: Just include the javascript in your html
Alternatively, if you don't care if the JS is loaded last, then you can just add the include helper at the bottom of the new.html.erb:
#messages/new.html.erb
#Other view stuff here
<%= javascript_include_tag "messages_new.js" %>
From what i understand, you want to have new.html and new.js to be served when requesting /messages/new.
It will not work, the rails controller will use js or HTML and never both.
HTTP does not work like that. You have only one file that is send in response to an HTTP request and you cannot send 2 at the same time.
The purpose of the respond_to block of the action is to use html for html request (classic browsing) and js when the request is for js, and that's what you use for ajax.
For what you want, you need to add a js reference in your erb, there is no other way.
Sorry I'm late but you can call the JavaScript in "/messages/new.js.erb" by using:
messages/new.html.erb
link_to "Link", "", remote: true
Any time you click that link it will fire "/messages/new.js.erb".
Javascript code will run from your html.erb file if you wrap it in script tags. If it's for testing purposes only and just temporary, this might be a quick and easy way to fire your jquery with your html rather than creating a new, separate file.
Related
I am having a problem loading/rendering js.erb files when the page loads. It always displays as html.
This is what I have tried adding to my html.erb file on which I want my js.erb to load on:
<%=render 'employees_js.js.erb'%>
<%=render(:partial => 'employees_js.js.erb', :handlers => [:erb], :formats => [:js]) %>
It might be worth noting that I am trying to load this file on a template that is being loaded with AJAX.
Please let me know how I could load the js.erb file as js.erb and not .html.
Thank you.
Please let me know if I wasn't clear enough.
You can't render a js partial in your html.erb with the render method. Render is going to be looking for an html file in this context. One workaround is to change your partial name to employees_js.html.erb, then wrap the javascript in a script tag.
<script type="text/javascript">
// your script
</script>
Then:
<%= render 'employees_js' %>
Sidenote: Rending javascript in rails should be done when it is explicitly requested, usually with a remote: true link or form. Otherwise, your javascript should be in the asset pipeline and requested with the javascript_include_tag.
I am trying to render a collection this way
views/consumer/home/index.html.erb
<%= render #promotions %>
consumer/promotions/_promotion.html.erb
<% content_for :page_js_modules do %>
<%= javascript_include_tag 'consumer/carousels' %>
<% end %>
Since _promotion.html.erb is rendered more than once if there are more than one promotions in #promotions, the javascript tag also gets included more than once and causing issues.
I want to put the javascript tag inside of promotion view only so as to make it modular and hence anyone can use it without worrying about including the pertaining js tags.
Since _promotion.html.erb is rendered more than once if there are more than one promotions in #promotions, the javascript tag also gets included more than once and causing issues.
This is what partials are for, to reuse same code. You can't use (you can but it won't be a good practice) a js tag inside a partial because it will keep on rendering content inside the partial for each record in collection.
I want to put the javascript tag inside of promotion view only so as to make it modular and hence anyone can use it without worrying about including the pertaining js tags.
I think a better approach would be to have a partial inside promotions which would contain your js tag and render promotion partial
#consumer/promotions/_promotion_with_js.html.erb
<% content_for :page_js_modules do %>
<%= javascript_include_tag 'consumer/carousels' %>
<% end %>
<%= render #promotions %>
if there are other use cases where you just want to include js instead of promotions partial then you can always separate out that in another partial and use it.
You probably want to include that script tag in your layout, not in a partial. Layouts are a good place to include scripts and stylesheets. You will incur only one call and the response will be cached. Best to call the script once only and rely on that cache, even if the layout calls it needlessly.
I have two views, one called dashboard and the other called home, both have their respective index.htm.erb files. The corresponding JavaScript files in the assets/javascripts folder do not seem to map to these views. For example, when I run an alert in home.js and then navigate to dashboard.html.erb the alert still runs.
How do I remedy this to get the functionality I am looking for?
I am confused as to how to write page specific raw JavaScript in Rails.
Edit: I could use the public folder but I had some ancillary issues with that and hence why I started using the files in the assets folder.
One of Rails' "opinions" about javascript is that it should be all concatenated into a single file (application.js), minified, and served to the client. This is an effort to minimize the number of requests the client needs to make when accessing your application (the goal is to have only three, one for your html, one for css, and one for javascript [excluding whatever images may be on the page.])
If you look in your application.js, you'll see a note saying that it is a 'manifest' file, and a line that looks like
require_tree .
Which says "load every javascript file in this folder into this file"
In order to load up some page-specific javascript, you'd need to
put a separate javascript file into app/assets/javascripts [call it custom.js, say]
stub out loading that file into the application manifest by writing stub custom in application.js
Include the custom javascript manually in your view (or, more better probably, a layout which renders your view): <%= javascript_include_tag 'custom' %>
However, I'd encourage you to look at whether you really need to separate this javascript, or whether it's a problem that can be solved by simply localizing your script to the page(s) it's intended for, which will keep the same functionality and keep your loads times ever-so-slightly faster.
$('body.some_custom_class').ready(function() {
alert('I'm running on this page!');
});
you can use content_for in your layout
app/views/layouts/application.html.erb
<html>
<head>
<title> ...</title>
....
<%= yield :javascript %>
....
</head>
....
</html>
and on your view app/views/home.html.erb
<% content_for :javascript do %>
<%= javascript_include_tag 'home' %>
<% end %>
other view content
you would also need to look at application.js and remove the //= require tree . line
My Problem
I am developing a Rails site and would like to use Javascript to render partials in some of my views depending on certain conditions. For instance-- I'd like to have a partial view of a Devise log-in/sign-in prompt come up in a modal box if the user is not signed in when accessing certain pages.
I've had a lot of trouble figuring this out-- the first issue was that I tried using render in the asset pipeline which after some research found doesn't work to begin with.
I then tried putting a js.erb file into my public/javascripts folder but javascript_include_tag force appends '.js' to the file name and using regular src=/javascripts/... didn't render the '.erb' stuff but rather it would append the text <%=j render :partial ... %>
My Solution
I have come up with this solution here and would like to know if there is a better solution to keep clean code. I am going to have a few other Javascripts that will render the same thing over different views.
I have created a app/views/shared/javascripts directory where I will put [filename].html.erb files.
To get the Javascript to run correctly I will <%= render :partial => 'shared/javascripts/...' %> wherever I want that script to be run.
Inside that script is something like:
<script>
$(document).ready(function() {
...
$(.class).append("<%=j render :partial => 'shared/modal' %>");
...
});
</script>
Is There a Better Way?
As far as I can tell-- this will do what I want it to do. I'm just afraid that I'm looking at this all wrong. I'll be working on another part of the app for a while and I really hope to either verify that this is acceptable and decent or find the proper way to ensure that I can use ERB in my JS files.
I know this answer is 3 years late for the OP, but I found this question googling something else and thought I'd answer it as it's something we do quite a lot in our legacy app.
Suppose you want to render the javascript alert("Hello, world!"). You can create the partial _greetings.js.erb in the folder app/views/my_resources:
alert('<%= #msg %>')
And call it from a controller action:
#msg = "Hello, world!"
render partial: 'my_resources/greetings', formats: :js
Hope that helps someone.
One way to do this is to put your partial in your javascript and render it using something like Mustache. The problem with this approach is that if you're rendering a partial you're also rendering traditionally elsewhere in the Rails app, the Mustache template could easily get out of sync with the Rails partial.
Another way to do this would be to have an action which returns the rendered partial as a string, either as XML or wrapped in JSON, and use AJAX to request the rendered partial from that action. Then your Rails controller will handle both rendering the template (using render_to_string) and wrapping it in JSON for your javascript to consume and redisplay.
Ok Im new to rails in general and these default loaders and cache loaders an all that stuff they make some sense to me. But my question is. If I want to include a certain JS file or 2 or specific script on a particular page how would I do that.
with rails I have
app/views/layouts/application.html.erb in this file I gather is the base template for my site/service, and in there I would put all the moving parts. However I have an occasional need where I only need a javascript file loaded on a particular page. But it needs to be called after the jquery file so jquery is loaded into memory prior to the file I want loaded. So Im not exactly sure how I would approach that. Cause in the layout I have the javascript loader line whatever it is exactly I dont remember but its :default none the less which means jquery and applications will load out by default from what the API tells me.
Which does bring me to another question the guy who initially set up the rails server we have added a file to the defaults I would like to mimic that but don't know how with that either.
The simplest way would be to just include the script file in the view where you need it. jQuery will have already been loaded in the layout.
Alternatively, you can use content_for, as ctcherry mentions. You can find a more detailed explanation here: Javascript Include Tag Best Practice in a Rails Application
Also, regarding you last question, I'm not sure I understand it correctly, but you can add more options to the javascript_include_tag separated by a comma:
javascript_include_tag :defaults, "my_other_file", "etc"
content_for might help you, look at the example that includes the piece of code: <%= yield :script %>
Alternatively, think about ways to allow the JS code to detect if it is begin executed on the correct page (maybe a class or id set on the body tag), and only execute if that condition is met. Then you can serve your entire javascript collection compressed and minified to the user's browser on the first page load, increasing site performance.
May be used for this:
<head>
<title>My blog</title>
<%= yield(:head) -%>
</head>
And send it there from a view:
<%- content_for(:head) do -%>
<%= javascript_include_tag :defaults -%>
<%- end -%>
It's good work!