Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is rails not rendering the .js.erb file?

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?

like image 627
nwestfall Avatar asked Oct 24 '13 15:10

nwestfall


2 Answers

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" %>
like image 72
Rupert Madden-Abbott Avatar answered Nov 02 '22 23:11

Rupert Madden-Abbott


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.

like image 38
Syl Avatar answered Nov 03 '22 00:11

Syl