I\'m trying to replace the content of a div after clicking on a link using Rails 3, remote_link :remote => true
and jQuery.
So far, I\'ve been able to ge
I'm going to propose an answer because comments don't allow for any formatting. Here is is: Something is happening on the server side and jQuery is not getting what you think it is. Here's an excerpt from the jQuery documentation:
error(jqXHR, textStatus, errorThrown)Function A function to be called if the request fails. The function receives three arguments: The jqXHR (in jQuery 1.4.x, XMLHttpRequest) object, a string describing the type of error that occurred and an optional exception object, if one occurred. Possible values for the second argument (besides null) are "timeout", "error", "abort", and "parsererror". When an HTTP error occurs, errorThrown receives the textual portion of the HTTP status, such as "Not Found" or "Internal Server Error."
That implies that your controller may be responding with something other than the expected data. In that controller, try:
Rails.logger.debug render_to_string(:partial => "followings/follow")
In any case, check your logs to make sure what you think is happening really is happening. Also, write a test to verify this:
# controller spec... modify if using Test::Unit
it "sends cool javascript" do
xhr.post :unfollow, :id => 83, :data-method => "delete"
response.body should == "some known response"
end
Ok, it's a hacky, brittle spec, but it will do until you know where things are going wrong.
Once you get this working, everything else will fall neatly into place.
I was also getting odd parsererror
s even though my remote links were correctly pointing to actions with :format => :js
and my controller actions were correctly using respond_to
to serve up JSON objects like:
respond_to do |format|
format.js do
render :json => {:something => "OK"}
end
end
The solution ended up being just dropping this line into my application.js
:
$.ajaxSettings.dataType = "json";
By default, it appeared that jQuery was trying to evaluate all responses as "script"
, which I guess means it was trying to execute it as code—? Dropping this line in once fixed the issue globally.
Instead of using the global $.ajaxSettings.dataType = 'json';
setting a cleaner solution is to add a data-type
attribute to the form element. The remote form event handlers will pass this through as the dataType to the jQuery AJAX call, e.g:
form_for @subject, :remote => true, :html => {'data-type' => 'json'}
instead of format.js
use format.json
setting $.ajaxSettings.dataType = 'json';
is liable to cause issues in other parts of your code.
Another way to solve this is to add .js to the form's action (if you're using a Rails link generator you can add :format => :json). Then make sure you are responding to json in your controller.
Here's an example of a sign in form configured that way:
<%= form_for User.new, :url => session_path(:user, :format => :json), :html => {:id => "login-form", :class => "well"}, :remote => :true do |f| %>
<label>Email</label>
<%= f.text_field :email %>
<label>Password</label>
<%= f.password_field :password %>
<%= f.hidden_field :remember_me %>
<%= button_tag "Sign in", :class => "btn", :type => "submit" %><%= image_tag "ajax-loader.gif", :style => "display:none", :id => "login-spinner" %>
<% end %>
In the controller:
def create
respond_to do |format|
format.html{ super }
format.json do
resource = warden.authenticate!(:scope => resource_name, :recall => :failure)
return sign_in_and_redirect(resource_name, resource)
end
end
end
If you are attempting to update the contents of a DIV using a normal JQuery AJAX call using the HTML output from a rails controller action, you need to tell JQuery what dataType to expect on the response, so that it will not parse the response as javascript and give you the parsererror you describe.
$.ajax({ url: "/blah",
contentType: "text/javascript", dataType: "html",
beforeSend: function(xhr) {
xhr.setRequestHeader('Accept', 'text/javascript');
},
success: function(data) {
$('your-div').html(data);
}
});
This would then be compatible with a controller action that uses the respond_to block:
respond_to do |format|
format.html {
# This would be a normal render of your template.
}
format.js {
# This would be a render of your template, as HTML, but only for your AJAX requests.
# We might use this to avoid including the layout of our template.
render :layout => nil
}
end