Monitoring a server-side process on Rails application using AJAX XMLHttpRequest - javascript

I'm using the following in the web page but can't get a response from the server while it's processing
<script type="text/javascript">
<!--
function updateProgress() {
//alert('Hello');
new Ajax.Request('/fmfiles/progress_monitor', {
parameters: 'authenticity_token=' + encodeURIComponent(AUTH_TOKEN),
onSuccess: function(response) {
alert(response.responseText);
fillProgress('progressBar',response.responseText);
}
});
}
//-->
</script>
<% form_for( :fmfile, :url => '/fmfiles', :html => { :method => :post, :name => 'Form_Import', :enctype => 'multipart/form-data' } ) do |f| %>
...
<%= f.file_field :document, :accept => 'text/xml', :name => 'fmfile_document' %>
<%= submit_tag 'Import', :onClick => "setInterval('updateProgress()', 2000);" %>
The 'create' method in fmfiles_controller.rb then happily processes the file and gets the right results (as per the submit button on the form). If I uncomment the '//alert('Hello')' line I get a dialog saying Hello every 2 seconds ... as expected.
However, the server never logs any call to 'progress_monitor' method in 'files' not even a failed attempt.
If I click the link
Run
it makes a call to the server, gets a response and displays the dialog, so I assume the routes and syntax and naming is all OK.
I really don't know why this isn't working. Is it because 2 methods in the same controller are being called via URLs?
I'm using Rails 2.1.0 in a development environment on OS X 10.5.5 and using Safari 3.1.2
(N.B. This follows on from another question, but I think it's sufficiently different to merit its own question.)

If you are not seeing messages in your log file for the call to 'progress_monitor' then it is possible that the request is never being sent.
Try this:
Try using the full URL instead of the relative URL for the Ajax.Request. I have had problems with relative URLs on some browsers with the Ajax.Request.
Enable Firebug or the IE Developer Toolbar. You should be able to see if the call to progress_monitor works or not. If there is a java script error then you will see the error clearly using these tools.

Related

j(render(#partial)) returns error: ActionController::UnknownFormat

I'm trying to render a partial with ajax, but for some reason it returns this error:
ActionController::UnknownFormat in ThingsController#upvoterandom
ActionController::UnknownFormat
I'm very confused because I accomplished something with an essentially identical format before, and I never had any problems with it. Does anyone see anything wrong with my code? I can render a string with the ajax; it's only when I try to render a partial that I get the error. By the way, I achieved this error by deleting the format.html line and then visiting the upvoterandom_thing path directly in my browser.
views/things/show.html.erb
<div id= "randomajax" >
<div id="randajax">
<%= link_to #rand.name, thing_path(#rand) %>
<%= link_to image_tag("UpArrowGray.jpg", class: "rand_up_vote"), remote: true, %>
<script type="text/javascript">
function reload_script() {
$(".rand_up_vote").click(function () {
$.get( "<%= upvoterandom_thing_path(:id => #rand.id) %>", function( data ) {
});
});
}
reload_script();
</script>
</div>
</div>
controllers/things_controller.rb I put asterisks around the line highlighted with the error.
def upvoterandom
#thing = Thing.find(params[:id])
#...
***respond_to do |format|***
format.html { redirect_to root_path }
format.js
end
end
views/things/upvoterandom.js.erb: .html("test") returns "test", so the problem has to be in the rendering.
$('#randomajax').html("<%= j(render(#randajax)) %>");
views/things/_randajax.html.erb
TEST
THIS IS THE OTHER NEAR-IDENTICAL AJAX STRUCTURE THAT WORKS:
views/things/show.html.erb
<%= form_for([#thing, #comment], remote: true) do |f| %>
<%= f.text_area :text %>
<%= f.submit "Post", id: "postacomment" %>
<% end %>
controllers/comments_controller.rb
def create
#thing = Thing.find(params[:thing_id])
#comment = #thing.comments.create(comment_params)
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
views/comments/create.js.erb
$('#comments_h2').prepend("<%= j(render(#comment)) %>");
views/comments/_comment.html.erb
TEST
I'll apologize up front for the long answer. I've tried reproducing your issue in multiple ways, but I think you might be looking in the wrong direction. I've included the entire story so you can see if it all matches up, and if it doesn't it hopefully leads to insights which will help you resolve the issue.
ActionController::UnknownFormat happens before view rendering
If Rails presents you with the ActionController::UnknownFormat error, it means that your controller does not respond to the format that is being requested. The error is raised by the respond_to method, at the exact line you highlighted.
Had an error been raised during view rendering, then it would have been bubbled up through either the line with format.html or format.js. So this error is certainly not caused in the view rendering part.
Reproducing the ActionController::UnknownFormat error
The only way I can get the exact error message you posted, is if I request the AJAX response page directly in the browser. Then you see the Rails error page, which will also show you which format the request was done with. You're probably requesting the page with a format other than html or js, in which case the ActionController::UnknownFormat error is triggered, since your controller only responds to the html or js format.
I think the actual issue is hiding somewhere else.
Reproducing from views/things/show.html.erb
I have tried to reproduce the error from the views/things/show.html.erb page as well. When doing this with your original code, I get a syntax error on the following line:
<%= link_to image_tag("UpArrowGray.jpg", class: "rand_up_vote"), remote: true, %>
This happens due to the comma after remote: true. Because of this, I assume you haven't been constantly testing with the remote link. When testing it without the remote: true, the reload_script function triggers a jQuery AJAX request. You're doing this with the following line:
$.get( "<%= upvoterandom_thing_path(:id => #rand.id) %>", function( data ) {
});
This actually triggers an XHR request with the format */*. It basically tells Rails that any format will do. Here's what happens:
The ThingsController responds with the first format you've defined in the respond_to block, which is the HTML format. This triggers a redirect to your root path.
jQuery follows the redirect, again using the */* format.
The controller at your root path responds with the first defined format, or HTML by default if no respond_to block is present.
jQuery then loads that response.
There's no way that the ActionController::UnknownFormat could have been raised from ThingsController when reproducing it like this.
What I think
This is mostly guessing work, so please correct me if I'm wrong:
You clicked the upvote remote link and nothing visibly happened.
You added a JavaScript to explictly fetch the page through AJAX, but still nothing visibly happened.
You visited the things/1/upvoterandom.js (or alike) page directly with your browser. In this case an ActionController::InvalidCrossOriginRequest would have been raised.
You visited the things/1/upvoterandom.json (I used JSON, but it could be any format other than HTML or JS) page directly with your browser, and you got the ActionController::UnknownFormat error.
If this is how it happened, you need to go back to step 1, and start reproducing the issue in another direction:
Go to the things/1 (or another Thing ID) page.
Open up the Developer Tools/Web Inspector of your browser.
Go to the view which shows the network communication (in Chrome this is the tab called Network).
Now click the link, and see what communication happens between your browser and your Rails application. If a lot happens, you often can filter for XHR only, which only show AJAX requests.
If you see a 500 status popping up there, you need to check the Rails server output for an error with stacktrace.
I can only guess what the actual cause of the issue is, but I think it might actually be some error when rendering the view, As you said, the only case where it doesn't work is if you render the partial. I hope this information helps you to resolve the issue.
In views/things/upvoterandom.js.erb you're trying to render #randajax like so:
$('#randomajax').html("<%= j(render(#randajax)) %>");
But I don't see #randajax being assigned anywhere, which means that it's nil.
Trying to render nil is the cause of this issue. But do confirm this by doing the following the first:
$('#randomajax').html("<%= j(render(nil)) %>");
If it returns the same error, then we've found the culprit.
Either assign #randajax something prior to rendering it or simply use:
$('#randomajax').html("<%= j(render(path_to_partial)) %>");
Well I haven't figured out why the syntax I was trying wasn't working, but this syntax does work:
$('#randomajax').html("<%= render 'randajax' %>");

Rails 3 make ajax using jquery and execute .js.erb

I have the following haml code:
%input{:value => "", :type => "button",:class => "SendBtn", :onclick => "$.get('#{send_path}',{parameter:$('#parameter').val()}); "}
This input executes an event in the controller.
// This is my controller
def send
if request.xhr?
// do stuff
end
end
But my js code in the corresponding .js.erb file is not being executed. It is returned as the response of the get request.
// send.js.erb
alert('hello');
How is the rails way to have this code executed?
Your problem is not Rails related, it's jQuery. With the get method you are just fetching more or less plain text. This will not get executed. You could do an eval on the text but there is a better way. Use the getScript method from jQuery. This will fetch and execute your code.
As a side note, there are two things that are bothering me in your code:
You are using inline JavaScript. try to remove this by using a data- attribute for your send path, like this data: { sendPath: send_path }, and retrieving it with $(yourInput).data('sendPath') in your application.js file.
From my personal view I do not like to put executing JavaScript code in ERB templates. I find that this fragments the front end logic of my app. For me it worked better to put the logic in .js files and communicate with the server over JSON.
As #topek said, you have to use $.getScript. Also in your situation better approach is to use button_to with :remote => true property instead of plain input.
<%= button_to "Do Something",
{ :controller => :somecontroller, :action=> :something },
{ :remote => true }
%>
Also you can pass attributes to button_to (but you have add parameter to your route definition).
<%= button_to "Do Something",
{ :controller => :somecontroller, :action=> :something, :param => #object.id },
{ :remote => true } %>
Here goes documentation for button_to: http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to

Unable to bind ajax:success to form created with form_for .... :remote => true

I'm using Rails 3.1.1
I have the following in a haml view:
= form_for booking.notes.build, :remote => true do |f|
= f.text_area(:content)
= f.hidden_field(:noteable_id)
= f.hidden_field(:noteable_type)
= f.submit('Add note')
Which creates new notes on submission. Also the response from my controller is appearing correctly in a Chrome console (Network tab). But I cannot seem to grab the response.
I want to update the note list on the page after submission. I've been trying to bind to the ajax response so I can grab the response, but I am failing. For example, I think this should work but does not:
$('#new_note').bind('ajax:success', function() {
alert('Hi');
});
But no alert is triggered. Which I think explains why this also doesn't work.
$('#new_note').bind("ajax:success", function(evt, data, status, xhr){
// Insert response partial into page below the form.
$(this).parent.append(xhr.responseText);
})
Can you please point me as to what might be going wrong?
Did you try 'ajax:complete'?
Other things that can go wrong here:
Status code was not really "successful". This triggers success in jQuery
if ( status >= 200 && status < 300 || status === 304 ) {
Or the event handler was evaluated before the form was rendered. Try event delegation:
$("body").on('ajax:success', '#new_note', function(){...})
(careful, this is the new jQuery syntax. If using old jQuery, adjust accordingly)
if you want, you can put your javascript in create.js.erb (code in this file will be executed after response will come to your browser)
And in this file you can use if statement, like
<% if #ok %>
//your code
<% end %>
if in your controller's action set #ok to false the response will be empty!
This is generally done by doing a create.js.erb file in your controller's view section (if haven't done so already) in which you have access to whatever variables come out of the create action, and there you can render your html
in your create.js.erb file you could write something like
$("#new_note").html('<%= escape_javascript(render partial: "path/to/partial") %>');
read more in this post I wrote some time ago, it pretty much explains the whole flow

How to make an AJAX call to different domains in Ruby on Rails 3.0

I have an action email in my controller of application running on www.example.com and I am trying to send the form data of email to www.data.example.com/email where my another application receives the request and I am able to save the data in js format. But I want to send back the acknowledgement to www.example.com and replace the html using rjs template. Here are some code for you reference:
email.html.erb called on www.example.com
<div id="div_content">
<%= form_for(#user, :url => "http://data.example.com/mail", :remote => true) do |f| %>
<%= f.label :email %>
<%= f.text_field :email%>
<% end %>
</div>
email action of application on : data.example.com/email -
def email
#user = User.create(params[:user])
respond_to do |format|
if #user.save!
format.html { redirect_to(user_page_path(#user.vip_id), :notice => 'Thank you! You are now on our priority list.') }
format.js
else
format.html { render :text => "user can not be saved at this moment!"}
end
end
end
email.js.rjs called on www.data.example.com/email
page.replace_html :div_content, :partial => "show", :object => #user
I can see in my log that request comes all the way from one domain to sub domain and even action gets triggered but, I can not get the response back to the main domain. So, is there any way to send a callback to main domain. I just want to reflect changes there at the form which is inside div_content div and want to replace with content of _show.html.erb which I have on my sub domain.
Many Thanks,
Surya :)
This is happening because rails thinks you are trying to launch a cross site request forgery attack against yourself. By default rails has a security feature baked in that rejects form submission from outside sources (ie your other app)
Easiest (but not the most secure), way around it would be to add this to the top of the controller you are posting the data to:
protect_from_forgery :except => :email
RE COMMENTS
Ahh I see, I was not paying very close attention when I first read your post, sorry about that. I missed all the parts about rjs.
I am certainly no expert on rjs but it looks like you are doing most everything right. Only suspicious part to me is this line:
page.replace_html :div_content, :partial => "show", :object => #user
I think it should be:
page.insert_html(:bottom, "div_content", :partial => "show")
Also you might want to try and replace the rjs template with
page.alert("debug");
Just to make sure its really not coming back, because I would suspect it is...
Guys here is what I got to make it work -
Well I was trying to do cross domain communication using my both RoR app as I mentioned right up there in my question!
Finally I have found a way to achieve the same using "EasyXDM" (www.easyxdm.net) it solved the problem. It works great on most of the browsers including IE7 and Firefox older versions.
CORS is another solution if you want to get it done, but it fails to work on IE7...
whoa! now I can rely on the cross domain communication between my different apps without using an iFrame.

Browser sometimes displays JSON text instead of desired page

I have been experiencing an intermittent issue in my Rails app, and I'm having trouble figuring out what is going on. When the user logs in, they see a dashboard that contains some JavaScript code which performs an AJAX call to an action. Occasionally, instead of seeing the dashboard, when the user logs in, they see the JSON response text from the action instead of the dashboard (in Chrome) or they download a .json file (Firefox). It's intermittent and doesn't usually happen, but it's really annoying when it does occur.
Here's a dumbed down version of some of the code:
JS running in the template head:
$(function () {
var remoteLink = $('#remoteLink');
remoteLink.live("ajax:complete", function () {
setTimeout(function () {
loadCount();
}, 30000);
});
loadCount();
function loadCount() {
remoteLink.click();
}
});
And the link in the template:
<%= link_to 'get count (hidden)', {:controller => 'something', :action => 'count'},
:id => 'remoteLink', :class => 'hidden', :remote => true, 'data-type' => 'json' %>
And the controller action:
def count
render :json => get_counts_function_returning_a_hash
end
My hunch is that it's a race condition -- perhaps related to the use of setTimeout? -- but I haven't been able to verify that hunch. Can anyone tell me what might be going on here? I've seen this in the wild on other sites, too -- also intermittent, not generally occurring but annoying when it happens.
Do you have any authentication code that uses store_location or similar functionality to redirect the user to a specific page after they have logged in?
I once had a similar problem where the AJAX callback was causing store_location to store the JSON URL and the user was redirected to the JSON response rather than their dashboard. This was hard to track down because the user had to sit idle at the page for some time before the bug was manifest.
In any case, I suspect it is something like this rather than a problem with Rails.
I would suspect the Content-Type of the HTTP response and check any places you are setting it for possible bugs. Run your browser with an extension for tracing HTTP headers to capture what the values are when it does occur.

Categories

Resources