Ruby on Rails 3: AJAX call not refreshing div with link_to - ruby-on-rails

I am new to Ruby on Rails and i am working through a few example applications in the O'Reilly Head First Rails book. In one of the examples there is a page made up of three partials. The middle partial is a list of items. There is a link right below this section that, when clicked, should refresh the div containing that partial. The book is running examples based off of Rails 2.3 i believe and i am using Rails 3.1. This is the example that the book is giving me:
routes.rb:
map.connect '/flights/:flight_id/seats', :action=>'flight_seats', :controller=>'seats'
seats_controller.rb:
def flight_seats
#flight = Flight.find(params[:flight_id])
render :partial => "flights/seat_list", :locals => {:seats => #flight.seats}
end
show.html.erb:
<div id="seats">
<%= render :partial=>"seat_list". :locals=>{:seats=>#flight.seats} %>
</div>
<$= link_to_remote("Refresh Seats", :url=>"/flights/#{#flight.id}/seats", method=>"get", :update=>"seats") %>
This example is also using prototype.js since that's what Rails 2.3 came with built in. Rails 3 has jQuery as the default JavaScript library. (not sure if that makes a big difference)
Here is what i have so far. This is getting the contents of the partial correctly, it's just not updating the "seats" div after the AJAX call gets the partial. My code:
routes.rb:
match 'flights/:flight_id/seats' => 'seats#flights_seats'
seats_controller.rb:
def flights_seats
#flight = Flight.find(params[:flight_id])
render :partial => "flights/seat_list", :locals => { :seats => #flight.seats }
end
show.html.erb:
<div id="seats">
<%= render :partial => 'seat_list', :locals => { :seats => #flight.seats } %>
</div>
<%= link_to "Refresh Seats", "/flights/#{#flight.id}/seats", :remote => true %>
Any idea why my <div id="seats"> won't refresh with the updated partial? I'm betting there is but i'll ask anyway, is something wrong with my code?

The :remote => true option is a bit weird if you aren't returning JSON data. You can wrap your HTML in a JSON object, though, which is what I typically do. Or if you want something closer to your existing code something like this should work for you:
<%= link_to "Refresh Seats", "/flights/#{#flight.id}/seats", :class => "refresh-seats" %>
In your javascript somewhere:
$(document).delegate(".refresh-seats", "click", function(e){
e.preventDefault();
$("#seats").load(this.href);
});

Related

Getting "Template is missing" error when attempting to render js.erb file

I'm new to Rails, and I'm having an issue where I can't render a .js.erb file. I think the root of the issue is that Rails' internal routing mechanism expects me to name and configure my files just so, but I'm missing one or two pieces, and I'm not sure how to look for what needs to be fixed.
I have an HTML view with a link to a controller action:
<%# snip %>
<div id="holding_issues_list">
<%= link_to "Show issues on hold", {
:action => "show_user_issues",
:controller => "support",
:issue_type => "holding",
:user_id => #user.id },
:remote => true %>
</div>
<%# snip %>
I think (but I'm not sure) that :remote => true causes the link to make an AJAX call.
This is the corresponding controller action in the controller app/controllers/support_controller.rb:
def show_user_issues
#target_div = params[:target_div] || "holding_issues_list"
user = User.find(params[:user_id])
issue_type = params[:issue_type]
#snip - set the value of #issues
end
I want this file, named show_user_issues.js.erb and placed in app/views/support, to be rendered when the controller exits:
$("#<%= #target_div %>").show();
alert('test');
$("#<%= #target_div %>").html(
"<%= escape_javascript render :partial => '_show_user_issues', :locals => {:target_div => #target_div, :issues => #issues} %>");
This is app/views/support/_show_user_issues.html.erb, the partial I want show_user_issues.js.erb to render:
<% for issue in #active_issues %>
<div id="issue_<%= issue.id %>_display">
<%= render :partial => 'show_issue_mini', :locals => {:issue => issue} %>
</div>
<% end %>
When I try clicking the link in my original HTML view, nothing happens. When I open it up in a new tab, I get this error message:
Template is missing
Missing template support/show_user_issues,
application/show_user_issues with {:locale=>[:en],
:handlers=>[:builder, :erb], :formats=>[:html]}. Searched in: *
"/home/<>/app/views" *
"/home/<>/gems/kaminari-0.14.1/app/views"
The alert('test') that I put into show_user_issues.js.erb doesn't show up, so I think that Rails is getting hung up on rendering that file - that is, the routing mechanism can't find it. How can I correct this issue?
P.S. I double-checked that I put in all the file names exactly as they are in the code base.
Change your controller action to handle the type of request.
def show_user_issues
#target_div = params[:target_div] || "holding_issues_list"
user = User.find(params[:user_id])
issue_type = params[:issue_type]
#snip - set the value of #issues
respond_to do |format|
format.js
end
end
This will check the format of the request which is .js in case of :remote => true. So it will handle it by rendering the show_user_issues.js.erb file.
A couple other problems that I ran into after applying Manoj Monga's answer that I suspect other new Rails devs might run into:
In show_user_issues.js.erb, I had
[...].html("<%= escape_javascript render :partial => '_show_user_issues',[...]
The underscore before '_show_user_issues' caused the ERB builder to fail. It should have just been 'show_user_issues'.
In _show_user_issues.html.erb, I had
<% for issue in #active_issues %>
If you look closely at show_user_issues.js.erb, though, I named the variable #issues, not #active_issues:
[...]:locals => {:target_div => #target_div, :issues => #issues}[...]
So I changed the line in the HTML partial to
<% for issue in #issues %>
After these last couple changes, the new functionality I was adding worked as expected.

Javascript inside view in rails 3

I need to put some javascript inside a view. Basically I am having a play with the Recurly.js library.
Here is an example form: http://pastie.org/3142536
I have tried adding the JS to a partial: filename _recurly.js.erb
<%= render :partial => 'recurly.js', :locals => { :company => #company } %>
But its just outputting the JS to the page.
Is there a better way of doing this passing vars to JS for output and how can I get it to render the JS ?
Hope you can advise.
You need to use the function escape_javascript.
so your code should be:
<%= escape_javascript(render(:partial => 'recurly.js', :locals => { :company => #company }) %>
have you tried:
<%= raw(render :partial => 'recurly.js', ...) %>
Normally ERB will escape the output. Not sure if this is a good solution in the long run, even if it works, though.
why don't you use normal way of including javascript in your ERB view.
<%= javascript_include_tag "filename_from_public_javascripts_folder" %>

Ruby On Rails 3, form_tag remote on responding to Ajax request

thanks for reading this post. I've been stuck on an issue with RoR for the past few days. I have a form under index.html.erb as:
<head>
<title>Ajax List Demo</title>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
</head>
<body>
<h3>Add to list using Ajax</h3>
<% form_tag :action => :list , :method=>:get, :remote=>true do %>
Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
<%= submit_tag "Find" %>
<% end %>
<div id="my_list">
</div>
</body>
In the controller I have:
def list
puts "here!!!!"
reader = Reader.new
#profiles = reader.processURL(params[:url]) #profileList =
respond_to do |format|
#format.html { render :partial=>true, :locals => { :profiles => #profiles}}#{ render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}}
format.js {render :content_type => 'text/javascript', :locals => { :profiles => #profiles}}
# index.html.erb
# format.rss render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}
end
And a js file for remote UJS as list.js.erb
$("#my_list").html("<%= escape_javascript(render(:partial => "list"))%>");
The issue is I cannot get the results to render the partial _list.html.erb, in the div tag my_list. I get a blank page, 406 error. If I un-comment the render html code in the controller I get the partial back rendered in the browser. I am kind of stuck, I want to submit the form and the results to pop in the my_list div. I'm new to rails so if I'm missing something obvious don't hesitate to point it out to me....I'm definitely willing to try.
Changed it to this:
<html>
<head>
<title>Ajax List Demo</title>
<h1>Listing posts</h1>
<%= javascript_include_tag 'jquery.js' %>
<%= csrf_meta_tag %>
</head>
<body>
<h3>Add to list using Ajax</h3>
<% form_tag :action => :doit , :method=>:get, :remote=>true do %>
Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
<%= submit_tag "Find" %>
<% end %>
<div id="my_list">
</div>
Controller:
def doit
puts "here!!!!"
reader = Reader.new
#profiles = reader.processURL(params[:url])
respond_to do |format|
# format.html {render :partial=>true, :locals => { :profiles => #profiles}}#{ render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}}
format.js #{render :content_type => 'text/javascript', :locals => { :profiles => #profiles}}
# index.html.erb
# format.rss render :partial=>'profiles/list',:layout => false, :locals => { :profiles => #profiles}
end
JS
_doit.js.erb
$("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");
And finally a partial:
_doit.html.erb.
However I am still getting the 406 error, I dont have a duplicate _doit js or erb. Does anything standout as incorrect from this? Thanks again!
Another update:
I think the form is not rendered correctly:
This rendered:
<% form_tag :action => :doit , :remote=>true, :id => 'myform' do %>
Enter the public url:<%= text_field_tag 'url' ,'', :size => 80 %>
<%= submit_tag "Find" %>
<% end %>
This:
<form accept-charset="UTF-8" action="/home/doit?id=myform&remote=true" method="post">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="MLuau4hvfdGO6FrYCzE0c0JzwHhHKZqjmV49U673sK8=" />
</div> Enter the public url:
<input id="url" name="url" size="80" type="text" value="" />
<input name="commit" type="submit" value="Find" />
<input name="commit" type="submit" value="Find" />
Its adding my remote tag and id to the query string, isnt this wrong?
Ok finally got a clue forms need to be bracketed:
<%= form_tag( { :action => 'doit' }, :multipart => true, :remote=>true, :id => 'myform' ) do %>
Ok last update tonight:
Now I get in the logs:
Started POST "/home/doit" for 127.0.0.1 at Wed Oct 27 22:40:55 -0400 2010
here!!!!
Processing by HomeController#doit as JS
Parameters: {"commit"=>"Find", "url"=>"http://www.facebook.com/people/James-Stewart/653161299", "authenticity_token"=>"MLuau4hvf
dGO6FrYCzE0c0JzwHhHKZqjmV49U673sK8=", "utf8"=>"Γ£ô"}
Rendered home/_doit.html.erb (4.0ms)
Rendered home/doit.js.erb (9.0ms)
Completed 200 OK in 807ms (Views: 40.0ms | ActiveRecord: 0.0ms)
I see as JS and it says it renders my js/partial. However I am getting nothing on my_list div. My JS file:
$("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");
My html.erb form file has now:
<script$('#myform').bind('ajax:success', function(evt, data, status, xhr){
xhr.responseText;
});></script>
Its like the form does nothing, which is a good sign, no more 406 error. I know this is close, if anyone can point what I need to do in the js that would be great otherwise I'll take a break and try tmrw.
Ok I think its getting a response back just not rendering as you pointed out would be the issue yesterday Steve.
Debugging the JS on Firebug I see the html I want rendered in the div, for this:
http://localhost:3000/javascripts/prototype.js?1285674435/event/seq/1
Which means I think I am getting the JS response back now.
I have this on the form page:
<script>$('#myform').bind('ajax:success', function(evt, data, status, xhr){
$('#my_list').html(eval(xhr.responseText));
});</script>
Inspections say it doesnt know what myform is, but I put :id => 'myform' in the Rails code.
Again all thanks, I got a ton of help here and I want to share how I finally got it working back to the community.
The, js file for the method doit(def. need a better controller action name) is doit.js
The code was ultimately:
$("my_list").update("<%= escape_javascript(render(:partial => "doit"))%>");
For some reason leaving it as #my_list wouldn't be found in firefox, I had to use firebug to finally figure this out.
Obviously this is different from the way suggested below, and I am going to place the js script back into the form and remove the .js.erb file and see how that works works. I suppose I just render the partial in the format.js response? Also where does everyone find info on writing the UJS files? I know nothing about the syntax for anything starting with $.
Again thanks for the help, finally feel like I am making progress on learning rails.
I posted this answer on Hacker News, but figured the Stack Overflow community might benefit as well :-)
In Rails 3, the javascript drivers are very hands-off (i.e. unobtrusive). The problem you're having is that your app is returning to the browser a string of javascript, but there is nothing in the page that is then executing that javascript in the context of the page.
The rails.js ujs driver binds to forms and links with data-remote=true, which is what the :remote => true is doing, to make them submit their requests remotely, but that is where the Rails magic stops.
The good news is that the remote requests fires off some events you can bind to, which give you access to the data returned by the server (which fire off in the following order):
ajax:before
ajax:loading
ajax:success
ajax:complete
ajax:failure
ajax:after
You need to bind an event to the ajax:success event of your form. So, if your form had the id "myform", you'd want something like this on your page:
$('#myform').bind('ajax:success', function(evt, data, status, xhr){
eval(xhr.responseText);
});
xhr.responseText is what your server returns, so this simply executes it as javascript.
Of course, it's proper to also bind to the failure event with some error handling as well.
I usually don't even use the action.js.erb method of returning javascript, I just have my controller render the HTML partial, and then I have a binding like this in the page:
$('#myform').bind('ajax:success', function(evt, data, status, xhr){
$('#target-div').html(xhr.responseText);
});
I'm actually in the middle of writing a full article on this, but hopefully this is enough to get you going.
EDIT: I finished that article, fully explaining remote links and forms in Rails 3. Maybe it will help:
Rails 3 Remote Links and Forms:
A Definitive Guide
If you look at the rendered source of your page, you should notice an error in the fields attributes.
The correct way to do it is as follows:
<%= form_tag({:action => 'list'}, :remote => true %>
Notice the curly brackets, very important! Alternatively you could have done:
<%= form_tag('list', :remote => true %>
Do you have 2 partials named '_list'? Maybe that's causing problems and you should just a little more specific:
$("#my_list").html("<%= escape_javascript(render(:partial => "list.html.erb"))%>");
I'm not sure if this helps, but are if you using in IE be aware that IE sends some headers that screw with how your controller responds. So you may be sending an Ajax request with IE, but your Rails app thinks its just a plain html request.
I've had to setup jQuery to first erase the current headers and then add just the javascript header:
$.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept",'');xhr.setRequestHeader("Accept", "text/javascript")}
})
Using list as your function name in the controller may be the problem. That name is used internally by Rails.
Rails 5.1 introduced rails-ujs, which changes the parameters of these event handlers. The following should work:
$('#myform').bind('ajax:success', function(event) {
const [_data, _status, xhr] = event.detail;
});
Source:https://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html#rails-ujs-event-handlers
I know this is an old question but someone can benefit from it.
I think the error is related to this:
JS _doit.js.erb $("#my_list").html("<%= escape_javascript(render(:partial => "doit"))%>");
And finally a partial:
_doit.html.erb.
You are creating a _doit.js.erb partial to respond to the action doit in the controller but what you need is a view called doit.js.erb (without the underscore). Conceptually, the format.js in your action will respond to a view with the same name of it with extension js.erb.

updating a select box based on another (Ruby on Rails)

I'm in need of a more complete example on how to update a select box based on the results of a second select box in Ruby on Rails. I asked about this already here. I've read through the feedback from that posting, but am not having any luck figuring this out, and I've been trying for hours. Anybody know of a better (and more complete) example?
This is generally handled in Javascript. I don't particularly enjoy coding Javascript, so what I do for this in my application is to use a form_observer (a Rails helper which uses the Prototype Javascript library to watch your form for input changes) and have update a DIV in the HTML containing the second select box, based on the results of an AJAX call. Since AJAX talks to my server, I can write arbitrarily complex logic in Ruby to render the new HTML.
Example code:
#goes in view
<%= Code to render the first list box. %>
<%= render :partial => 'second_list_box_partial', :locals => {:selected = insert_magic_here } %>
<%= observe_field(:first_list_box,
:url => { :action => :second_box_ajax }),
:frequency => 0.5,
:update => :second_list_box_div,
:with => %Q| 'value=' + $('first_list_box').value; |
%>
#goes in controller
def second_box_ajax
first_box_value = params[:value]
#magic goes here
#selected = #more magic
render :partial => 'second_list_box_partial', :locals => {:selected => #selected}, :layout => false
end
#goes in partial
<div id="second_list_box_div">
Actual code to render list box goes here.
</div>

Rails AJAX: My partial needs a FormBuilder instance

So I've got a form in my Rails app which uses a custom FormBuilder to give me some custom field tags
<% form_for :staff_member, #staff_member, :builder => MyFormBuilder do |f| %>
[...]
<%= render :partial => "staff_members/forms/personal_details", :locals => {:f => f, :skill_groups => #skill_groups, :staff_member => #staff_member} %>
[...]
<% end %>
Now, this partial is in an area of the form which gets replaces by an AJAX callback. What I end up doing from the controller in response to the AJAX request is:
render :partial => "staff_members/forms/personal_details", :locals => {:skill_groups => #skill_groups, :staff_member => #staff_member}
However, if I do that then the form breaks, as the FormBuilder object I used in the form_for is no longer available. Is there any way for me to use my custom FormBuilder object inside a partial used for an AJAX callback?
Use fields_for inside your partial. It performs a similar task but without wrapping the form tags. See the API docs.
how about this?
#template.with_output_buffer do
#template.form_for #model_object do |f|
f.fields_for :some_nested_attributes do |ff|
render :partial => 'nested_attributes', :object => #model_object, :locals => {:form => ff}
end
end
end
this would be especially useful is you need to use the nested fields_for in the partial
You could instantiate a new instance of your form builder in the controller, though it feels sort of lousy to me:
# in the controller
render :partial => {
:f => MyFormBuilder.new(:staff_member, #staff_member, template),
:skill_groups => #skill_groups,
:staff_member => #staff_member
}
Alternatively, you could move more of the update logic to be client side which wouldn't require you to worry about rendering anything at all. You could just update the values via JS. Not sure if that works for your project though.
Maybe I'm a little late in the game here, and maybe I don't understand the question properly, but in ApplicationHelper.rb I think you can just add the line:
ActionView::Base.default_form_builder = MyFormBuilder
You can submit within your ajax call the content of f.object_name (it's also works with partials) and use it to render tags defined in http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html passing it as the first argument.

Resources