I’m dipping my toe into Rails3 / JQuery / unobtrusive javascript. I’ve written a toy app that does everything I want with JS disabled, now I want to introduce javascript so the server doesn’t have to refresh the entire page on every transaction.
Here’s the NON-JS Controller/View environment for creating a new Toy:
controller
class ToysController < ApplicationController
...
def new
@toys = Toy.all
@toy = Toy.new
respond_to do |format|
format.html # new.html.erb
end
end
views
# ==========
# file: views/toys/index.html.erb
<%= render :partial => 'list', :locals => {:toys => @toys, :target => nil} %>
<%= link_to 'New Toy', new_toy_path, :class => 'new' %>
# ==========
# file: views/toys/new.html.erb
<%= render :partial => 'list', :locals => {:dj_monitors => @toys, :target => @toy} %>
# ==========
# file: views/toys/_list.html.erb
<div class='table-div'>
<div class='body-div'>
<% toys.each do |t| %>
<%= render :partial => 'toy', :locals => {:toy => t} %>
<% end %>
<% if target && target.new_record? %>
<%= render :partial => 'form', :locals => {:toy => target} %>
<% end %>
</div>
</div>
(Pardon any apparent complexity, but all _list.html.erb does is list all the existing toys, and if :target refers to a new record, renders the _form partial at the end of the list.)
# ==========
# file: views/toys/_form.html.erb
<%= form_for(toy) do |f| %>
<div class='column selected'><%= f.number_field :sku %></div>
<div class='column selected'><%= f.text_field :description %></div>
<div class='column selected'>
<%= link_to 'Cancel', toys_path %>
<%= f.submit %>
</div>
<% end %>
ajaxification
What I’d like in the javascript enabled version is that clicking on the ‘new toy’ link (in index.html.erb) submit an XMLHttpRequest that returns only the form partial, which would be rendered in-place. The posting of that form would be a standard remote => true transaction as well.
But I can’t figure out how to get the server to return the form only. I modified ToysController’s new method to:
def new
@toys = Toy.all
@toy = Toy.new
respond_to do |format|
format.html # new.html.erb
format.js { render :partial => 'form', :locals => {:toy => @toy} }
end
end
… and enabled the :remote => true option in the [New Toy] link in the index.html.erb view:
<%= link_to 'New Toy', new_toy_path, :class => 'new', :remote => true %>
Now my proxy web debugger sees GET /toys/new HTTP/1.1 with X-Requested-With: XMLHttpRequest (that looks right), but the response is HTTP/1.1 304 Not Modified
Why isn’t it rendering _form.html.erb?
update: avoiding ‘304 Not Modified’ (non-)responses
There were two issues — @Robin solved the main problem. The other part was that I was getting a “304 Not Modified” response — it was technically correct, since the server had already sent the form — but made debugging difficult.
So if you’re trying to debug javascript / ajax code, it can help to stick a call to ‘expires_now’ in your controller to force the server to re-send the page while you’re debugging, like this:
def new
@toy = Toy.new
expires_now
respond_to do |format|
format.html ...
format.js ....
end
end
in new.js.erb
You would need to render the partial in the
format.jsif you did the request yourself: