Rails ajax:success callback not triggering in production - ruby-on-rails

My ajax callbacks are not triggering in my production environment. However, they are trigering in development with no errors. I'll simplify things for the sake of discussion.
Let's say I have a link that uses remote: true:
<%= link_to "Add Foo", new_foo_path, remote: true, id: 'new-foo' %>
foos_controller.rb
class FoosController < ApplicationController
def new
#foo = Foo.new
render partial: 'form' if request.xhr?
end
end
Using Chrome's console, I bind to ajax:success:
$(document).on("ajax:success", "#new-foo", function() { console.log("success!"); });
In development this works fine; I get the "success!" message in the Chrome console.
In production, however, it does not. The request is being made, the response is the form partial, but the callback does not fire.
Any ideas?
PS. The following does not work either.
$("#new-foo").bind("ajax:success", function() { console.log("success!"); })
There is no changes to config/environments/production.rb.
EDIT: It turns out ajax:error is being triggered when I click the link in production.
$(document).on("ajax:error", "#new-foo", function() { console.log("error"); });
My production logs don't show any errors, and the network tab in Chrome developer tools is showing a 200 OK response with the partial as the body.
However, the Content-Type header is text/javascript in production but text/html in development. Why is the same code responding with text/javascript in production?

The problem was that the browser was trying to execute the response body as JavaScript since the Content-Type header being sent back was text/javascript rather than text/html.
There are a number of ways to fix this. I chose to use jQuery's $.ajax function to set the dataType of all my ajax calls. This will ask the server to send back text/html.
$.ajaxSetup
dataType: 'html'
However, there are a couple other ways to ask the server for a specific response. See this StackOverflow answer for details.
There is still a lingering question, though. Why was the same code sending back text/html in development but text/javascript in production?

In case Google brought you here (like it did to me) because ajax:success is not working for you in a slightly different situation - when you're using button_to instead of link_to, this might save you some head banging.
Assuming you have this modified ERB:
<%= button_to "Add Foo", new_foo_path, remote: true, class: 'new-foo' %>
This JS won't work:
$(document).on("ajax:success", ".new-foo", function() { console.log("success!"); });
This won't work since the class new-foo is rendered on the input tag, while the data-remote="true" is rendered on the form tag. The ajax:success event fires on the form tag as a result -- not the child input tag, and that's why the console.log will never get called.
To fix this, change the class to form_class in the ERB:
<%= button_to "Add Foo", new_foo_path, remote: true, form_class: 'new-foo' %>

Related

Rails is rendering an empty partial from js.erb

I'm trying to render a partial from a js.erb file but nothing is shown in the view.
There is an AJAX call that triggers the report_announcement action in the announcements_controller when a button is pressed.
reports.js
$.ajax({
type: "POST",
contentType: "application/json",
url: "/announcements/report_announcement.js",
data: json_data
});
reports/_report_content.html.erb
<div id="report_announcements_container"></div>
announcements_controller.rb
def report_announcement
#announcement = Announcement.new(announcement_params)
respond_to do |format|
if #announcement.save
format.js {}
end
end
end
announcements/report_announcement.js.erb
I know that announcements/report_announcement.js.erb is rendering ok because I can see the logging statement in the console and also in the development.log as well as the report_announcement object being logged to the console.
<% announcement_json = #announcement.to_json.html_safe %>
var report_announcement = <%= announcement_json %>;
console.log('report_announcement');
console.log(report_announcement);
// this is where something is not right I think
$("#report_announcements_container").html("<%= escape_javascript(render partial: 'announcements/report_announcements', locals: { announcement: #announcement }) %>");
console.log('inside announcements....');
announcements/report_announcements.html.erb
This is where I'm having an issue because the logging statement for the partial is shown however nothing from the partial is shown on the page. I see in the development.log that the page rendered but nothing is actually shown in the view. The partial is not showing on the page in the report_announcements_container div.
<h1>test</h1>
<%= announcement %>
<script>
console.log('inside partial...');
</script>
You're using the wrong content-type in your ajax call. Rails recognizes both application/javascript and text/javascript as the :js format.
Also when you request a js format you actually have to execute the returned javascript for anything to happen in the client.
$.ajax({
type: "POST",
contentType: "application/javascript",
url: "/announcements/report_announcement.js",
data: json_data
}).done(function(data){
jQuery.globalEval(data); // Yeah eval is evil.
});
If you have done any "real" ajax calls before this will seem completely backwards - thats because it is. js.erb is a poor mans ajax framework for the javascript impaired which consists of a bunch of routes and views that mutate the DOM in the client.
Manually creating calls for a js.erb template is just a silly excerise in masocism. If you really want to use it create a form and use it send the request instead so that you can just leverage the javascript handlers that rails provide:
<%= form_with(url: "/announcements/report_announcement", remote: true) do |f| %>
# ...
<% end %>

Rails link_to with remote: true processing html instead of js after page refresh

I have a search page in my app, where there is an ajax search form. The search form works properly, passing parameters to the model to filter the search, and the model returning a collection of results. Upon search submit, #results are rendered on the page. Each #result then has a link to an action on it, like:
<%=link_to "Message", message_user_path(:id => user.id), :remote => true%>
Where this action in the controller is:
respond_to :js, :html
def message
#user_id = params[:id]
#user = User.find_by_id(#user_id)
respond_to do |format|
format.html
format.js
end
end
and this responds with message.js.erb, which triggers a messaging panel to pop up with a message to the user. All of this is working correctly, checking the log I see the correct get request sent, and the correct format being processed:
Started GET "/users/3/message"
Processing by UsersController#message as JS
However, if I refresh the page and try to click the same link that was working before, I get the error Template is Missing. Checking the log, I see that now there are two requests sent, first an html then the same js request.
Started GET "/users/4/message"
Processing by StudentsController#message as HTML
...
Completed 406 Not Acceptable in 3ms (ActiveRecord: 1.0ms)
Started GET "/users/4/message"
Processing by StudentsController#message as JS
The html request throws the missing template error. Does anyone know why refreshing the page causes rails to attempt to respond with an html request to a remote link?
EDIT: routes.rb
resources :students do
member do
get 'message'
end
end
Does your app/assets/javascripts/application.js contain
//= require jquery
//= require jquery_ujs
?
And your erb contains <%= javascript_include_tag "application" %>?
I was just struggling with a problem like this for HOURS and the last of those two points fixed it; I saw the first point mentioned in some other questions so I'll repeat it here.
Hope that helps.
(Credit where credit's due)
What solver it for me was adding :format => "js"
So in your case:
<%=link_to "Message", message_user_path(:id => user.id, :format => "js"), :remote => true %>
In general, when you use link_to on a particular button or so, when you press the button, as js request is send to the controller, but also searches for the respective .js.erb file and so on.
My solution was to replace
format.json do
with
format.js do
you can troubleshoot the request by setting a breakpoint (i use pry) in the controller and then look at the variable
request.format
For newer versions of Rails, this should be fixed where using remote: true within the link_to code, as the original poster was doing, will only look for a .js format to respond with. As others have said, if you never need an html response, then you can remove that from your code all together; you won't even need a respond_to, respond_with, etc as Rails will auto respond with JS looking for the template you already have made. So your controller code would look like this:
def message
#user_id = params[:id]
#user = User.find_by_id(#user_id)
end
And the link would still be this:
<%=link_to "Message", message_user_path(:id => user.id), :remote => true %>
Or this would work as well (my preferred way of syntax):
<%=link_to "Message", message_user_path(id: user.id), remote: true %>
This code will call the controller action which will look for the template message.js.erb.
I know this question is old now, but for anyone looking for answers and using current Rails 6+ (I'm using 7.0.0alpha), and if you are getting this same type of issue where both HTML and JS templates are being requested; check that turbolinks is not what is causing the issue. Sometimes turbolinks can cause a request to be sent twice and it may be sending the first request as an HTML request.
My form with remote: true was inside another form and I didn't know it.
So make sure it isn't inside another form.

Rails UJS link_to :remote does AJAX GET and normal <a href> GET

EDIT:
In addition to the helpful comments below, two excellent articles by Steve Schwartz explain everything clearly:
http://www.alfajango.com/blog/rails-3-remote-links-and-forms/
http://www.alfajango.com/blog/rails-3-remote-links-and-forms-data-type-with-jquery/
Rails 3.2.2 / jquery-rails 2.0.1 (uses jquery 1.7.1)
I have link which sends an AJAX request to add additional file upload fields to a form. Everything page-related works correctly - the new form field HTML fragment is retrieved from the server and appended to a div inside the form.
However,
The server receives two GET requests. One is the AJAX request ; the other appears to be the "normal" anchor element GET. I expected the "normal" GET would be stopped by Rails UJS.
Am I supposed to disable the normal link action myself? Can someone please explain what I have misunderstood, and what I should be doing instead [to prevent the second request]?
I've seen this question: Rails 3 UJS - controller gets called twice by link_to :remote. Accordingly, I tried changing the asset pipeline compilation config.assets.debug = false, but it had no effect. Moreover, this is not a double-AJAX GET request, so I think it's a different issue.
Thanks for any help!
Server log snippet:
Started GET "/files/new?asset_number=2" for 127.0.0.1 at 2012-03-23 15:23:27 +0100
Started GET "/files/new" for 127.0.0.1 at 2012-03-23 15:23:27 +0100
Browser HTML:
Add another file
View:
<%= link_to 'Add another file', new_file_path, :remote => true,
:update => 'files',
:position => 'after',
:id => 'add_another_file' %>
Controller's coffeescript:
$( ->
$('a#add_another_file').click( ->
url = '/files/new?asset_number=' + $('#files input').length
$.get(url, ((data) -> $('#files').append(data)), 'html')))
If you're adding a click event to a#add_another_file , then you don't need to use :remote => true , because you're making the Ajax request manually.
Also, the click event should prevent the default action from occurring. This can be accomplished by adding event.preventDefault(); to the beginning of the click event's callback. Note that the callback needs to accept the event argument.
If you want to use the :remote => true option, you should remove the custom click event that you've added. This is because the :remote => true option tells the *jquery_ujs* library to hijack clicks on a#add_another_file. Thus, you needn't make your own HTTP request.
Next, to dictate what's done with the response, bind to the various events that will occur on a#add_another_file, such as success and error.
Here's the full list of Ajax events that you can bind to.
It's actually pretty simple, your remote link_to is sending one request (without params) and you've added a click event on that link to send another one (with params).
You should simplify your link_to as:
<%= link_to 'Add another file', new_file_path, :id => 'add_another_file' %>
Then in your click event you should return false so it doesn't follow the url.
It looks like you're using a lot of unnecessary parentheses in your coffeescript.
$ ->
$('a#add_another_file').click ->
url = '/files/new?asset_number=' + $('#files input').length
$.get url, ((data) -> $('#files').append(data)), 'html'

Rails 3.1 with latest jQuery-UJS IE9 wants to save response as .js

I have a simple form for submitting an image like so:
<%= form_tag avatar_item_path(#tutorial, :format => :js), :method => :post, :remote => true do %>
<%= file_field_tag :item_avatar, :name => "item_image[image]" %>
<% end %>
(Dragonfly handles the image, in case that's somehow relevant)
My controller action is called update_avatar and it has no render or respond_to logic (it just calls find and save methods) so by default on a js request update_avatar.js.erb is rendered.
update_avatar.js.erb contains a js function and $(function () { // some code here })
In FF, Chrome and Safari this works fine.
The problem: IE9 wants to save the response as avatar.js
Using the developer tools I can see that the request is sent and received correctly and the response content-type is "text/javascript; charset=utf-8"
I've tried manually setting the content-type to application/javascript and this had no effect.
As others have suggested elsewhere, if I set the content-type to text/html IE9 will not prompt me to save it but in this case none of the browsers will actually evaluate the js in the response (I put the js in script tags).
From searching it seems that about a year ago others were experiencing the same issue and their solution was to upgrade jquery, since a year has passed I have a much newer version. Perhaps this is a regression?
I'm using the latest jquery-rails gem , 1.0.19, which includes jquery 1.7.1 and also includes the latest version of https://github.com/rails/jquery-ujs (I did a diff to make sure).
Using Jquery File Upload
https://github.com/blueimp/jQuery-File-Upload (basic version, not UI version)
Make sure the url has the js format explicitly stated and set the dataType option to 'script':
$('#item_avatar').fileupload({
url: "<%= avatar_item_path(item, :format => :js) %>",
dataType: 'script'
});
In your controller set the response content-type to text/plain:
respond_to do |format|
format.js { render :content_type => "text/plain" } # needed for IE
end
The js response should now properly eval in all browsers!

form not processing as JS

So I have the following rails form, and the corresponding controller action has respond_to :js, but rails keeps processing it as HTML. Not really sure how to proceed. Any ideas?
form_tag url_for(controller: 'posts', action: 'add_tag'), id: 'enter_tag', remote: true, style: 'display:none' do
text_field_tag :tag
end
UPDATE: removing 'display:none' fixes it (I was having the form fade in.) Why is this?
Chris, are you using Rails 3.1? If so, (I believe, i'm on 3.0.10 still) the jQuery UJS library is automatically includeed. If that's the case, it should be using a / datatype request, but I believe rails should notice it's an xhr request and send it to the JS response automatically.
A quick solution may be changing the url_for to url_for(controller: posts, action: 'add_tag', format: 'js') but I believe it should pick the JS response even without that. I don't like this approach though because I don't believe it would ever pick your HTML response for non-js Users.
Another fix may be to put format.js as your first response format in the controller (it may be HTML now), but I"m missing the reason as to why it's not recognizing it as an Ajax request to begin with?

Resources