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 th开发者_开发知识库e 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
.
精彩评论