Ruby on Rails3: How do I invoke javascript before an ajax event is fired with remote => true? - ruby-on-rails

<%= link_to( {:controller => 'board',
:action => 'take_turn',
:id => #board.id,
:x => col,
:y => row} , :remote => true, :onClick => "return links_disabled;") do %>
<div class="ttt_square">
</div>
<% end %>
in rails2, there were :before, and :complete params, but I have not found any documentation for this in rails3

As I understand it, this is one of the consequences of Rails 3 using UJS (unobstrusive javascript). Rails 3 enables you to keep the javascript away from e.g. a link-tag. Instead of the link-tag specifying what should be done via javascript, you make the javascript observe the link-tag.
You achieve this by binding a function to a certain event of an object, eg. binding the ajax:before event of the link-tag to a function.
In this blog post the author explains how to do it, in his case with JQuery.

As far as I understand, in Rails 3 you bind the callback events to the element on the client side, and they are fired by rails.js at the appropriate times.
$('#myform').bind('ajax:success', function(){
alert('I succeeded');
})

If I remember well, there is no more support in Rails3.
You could use native jQuery function:
ajaxStart()
http://api.jquery.com/ajaxStart/
See details here: http://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-in-rails-3/

My version (jquery-rails 0.2.6) supports ajax:before, loading, success, complete, failure, and after. The parameters to the success/failure functions are not the same which has tripped me up in the past. But the following works for me:
$('a').bind('ajax:loading', function() {
alert('here');
});
If your link element was created after the initial page load, you might need to bind using 'live':
$('a').live('ajax:loading', function() { alert('...'); });
I would also double-check that your onclick handler is not interfering.

Related

Rails 3: How to make an Ajax call?

I would like to have a link (is there a better option?) on my page which will make an Ajax request when clicked. (I would like to update a field in my database when the link is clicked.)
What is the simplest way to achieve this ?
Could you refer me to some tutorials ?
Really simple. In your view, have a link/button like so. Important bit being :remote => true
<%= link_to 'Update Thingy', update_thingy_path, :confirm => 'You sure you wanna update?', :remote => true %>
or
<%= button_to('Update Thingy', {:action => 'update_thingy', :thingyid => 314}, :method => :get, :remote => true) %>
Obviously, you have to get update_thingy_path to resolve to some action as normal. The difference is when you render you are going to be rendering some *.js.erb instead of *.html.erb. In that update_thingy.js.erb, you just put whatever javascript you want to run in the client. You might wanna notify the user that the update happened for example (in jQuery):
$('#notice').html("Thingy was update.")
Or if whatever javascript you're returning is really simple, in the controller you can do something like the following instead of having a whole js.erb for a one-liner.
render :js => "alert('Blah')"
You're really going to be using two technologies to accomplish this: javascript on the client-side, and rails on the server-side.
The general idea is that you want to:
(1) add your web methods on the server side with rails, and then
(2) use something like jQuery to get your client-side js calls up to the server to fire off the web methods.
Two writeups I found by googling for : rails3 ajax
http://www.stjhimy.com/posts/7-creating-a-100-ajax-crud-using-rails-3-and-unobtrusive-javascript
http://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-in-rails-3/
API Reference for using jQuery's ajax post() method:
http://api.jquery.com/jQuery.post/

:remote => true with url_for, How does one do this?

I am trying to make an entire div tag clickable. The code I am trying to use is below, and when I add the
:remote => true
bit it throws Too many args error, 2 for 1.
Code:
<div id="foo" onclick="window.location = '<%= url_for foo_controller_path(:someparam => #left), :remote => true %>'"></div>
url_for doesn't accept the :remote => true argument, it's usually the link_to method that you would send it to.
Is there a reason you can't make your <div> a link instead? For all intents and purposes it is functioning as a link, so you should mark it up as such, we call that semantic mark-up.
If you really wanted to do this it would probably be best to use jquery (or prototype, if that's your cup of tea) to perform the action unobtrusively... it makes it easier to do the ajax request too. Are you trying to update something on your page after the link is clicked, or just do nothing?
Also the 'window.location' is telling the javascript on the page to redirect. You wouldn't use that if you wanted to make the request remotely.
Using jquery you could do it like this if you want to stick with a div
%(function(){
$('#foo').click(function(){
$.get(
url: $(this).data('request-path'),
success: function(data){
alert('success sir! controller responded with ' + data);
}
);
});
});
And use this in your view:
<div id='foo' data-request-path='<%= url_for foo_controller_path(:someparam => #left) %>'></div>
But if you changed it to a link tag you could do this instead...
= link_to("", url_for(foo_controller_path(:someparam => #left)), :remote => true, :id => 'foo')
And it ought to just work. Then you can style this link the way that you were trying to style your div tag.
This is more semantic and less code for you to worry about. If you need to update something in the dom afterwards you can add this jquery (if you're using jquery, and rails3):
$('#foo').bind('ajax:success', function(data){
alert('successful request! data was: ' + data);
});
I didn't necessarily test all of this but it should be a good starting point... I'm not a big fan of putting onclick handlers into tags. It tends to work nicer when you bind events using jquery.
If you want to be able to do what i've described but you're in rails 2, you can get the rails3 jquery script from here: https://github.com/rails/jquery-ujs
Note that you'll also need that script if you're using rails 3 and want jquery instead of prototype (like me!). Rails 3.1 will come bundled with jquery instead of prototype I hear, by default.
And change the :remote => true in that url_for to "data-remote" => true (for rails 2, with rails 3 you can use the symbol syntax and it makes the 'data-remote' attribute for you.
Let me know if something didn't quite work or you need clarification. Or even if you hate my ideas alltogether :p

Rails controller not rendering correct view when form is force-submitted by Javascript

I'm using Rails with jQuery, and I'm working on a page for a simple site that prints each record to a table. The only editable field for each record is a checkbox. My goal is that every time a checkbox is changed, an ajax request updates that boolean attribute for the record (i.e., no submit button).
My view code:
<td>
<% form_remote_tag :url => admin_update_path, :html => { :id => "form#{lead.id}" } do %>
<%= hidden_field :lead, :id, :value => lead.id %>
<%= check_box :lead, :contacted, :id => "checkbox"+lead.id.to_s, :checked => lead.contacted, :onchange => "$('#form#{lead.id}').submit();" %>
<% end %>
</td>
In my routes.rb, admin_update_path is defined by
map.admin_update 'update', :controller => "admin", :action => "update", :method => :post
I also have an RJS template to render back an update. The contents of this file is currently just for testing (I just wanted to see if it worked, this will not be the ultimate functionality on a successful save)...
page << "$('#checkbox#{#lead.id}').hide();"
When clicked, the ajax request is successfully sent, with the correct params, and the action on the controller can retrieve the record and update it just fine. The problem is that it doesn't send back the JS; it changes the page in the browser and renders the generated Javascript as plain text rather than executing it in-place.
Rails does some behind-the-scenes stuff to figure out if the incoming request is an ajax call, and I can't figure out why it's interpreting the incoming request as a regular web request as opposed to an ajax request.
I may be missing something extremely simple here, but I've kind-of burned myself out looking so I thought I'd ask for another pair of eyes. Thanks in advance for any info!
In your controller you need to specify the proper response. Since you didn't post the controller I'll just try to fill in the blanks.
def update
# Update something
respond_to do |format|
format.js # this renders your rjs file
end
end
Specifying the format tells the rails app to interpret the javascript instead of just sending it back as text.
The other option instead of using rjs is to do an inline rjs block like this:
render :update do |page|
page.replace_html 'user_list', :partial => 'user', :collection => #users
page.visual_effect :highlight, 'user_list'
end
Only use the inline rjs if you will be doing minimal changes to the interface that can be put into one or two lines. Anything more should be in it's own rjs file.
This question is related to this one, but the answer varies slightly. I had to create a new way to submit the form, since the default jQuery submit() method does not submit as a 'script' and certainly does not fire the code that Rails generates in the onsubmit="..." handler via the form_remote_tag helper.
The solution was to create a new function as the linked answer suggests, but the contents are slightly different:
jQuery.fn.submitWithAjax = function() {
jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray()) + '&authenticity_token=' + encodeURIComponent('<%= form_authenticity_token %>'), dataType:'script', type:'post', url:'/update'});
return false;
};
This is brittle right now-- notice that I insert rails' form_authenticity_token into the Javascript, but really the method (post) and the url (/update) should also be generated rather than hardcoded.
Things are working A-OK now.

Where did link_to_function disappear to in Rails 3?

I was just playing around with Rails 3 beta and noticed that link_to_function is now gone. I presume there's an alternate method of achieving the same result (onclick event?) but I was wondering if there's a more Rails-3'y way of doing it. TIA.
Rails 3 seems to have done away with Prototype Helper in favour of a less obtrusive/JS library agnostic approach. The goal is to eliminate all inline javascript generated by Rails. Prototype Helper generated pretty much all of the javascript.
Now any of the non remote variants of helpers will generate the proper javascript for a remote call in your JS library of choice just by supplying the :remote => true option.
Unfortunately this doesn't hold true for the x to function methods. For the time being there are the prototype legacy helpers which are no longer a core part of Rails.
You could also use call as defined in ActionView::Helpers::PrototypeHelper::JavascriptGenerator::GeneratorMethods to supply javascript code to :onclick as an html_option for link_to, but that's not exactly pretty.
Examples:
Rails < 3 | Rails 3
link_to_remote "target", url | link_to "target", url, :remote => true
form_remote_for #post | form_for #post, :remote => true
etc....
Or something to that effect. I'm having trouble finding official documentation to back up my claims. So the release notes will have to do for now.
Before you can use it, you'll need to include the proper js source files. Ensure that you are loading prototype.js and rails.js, or the library and driver for JS framework of choice in their place.
Remember, Rails 3 is in beta now. But that doesn't mean it's done. I honestly have no idea how link_to_function will be replaced. It goes against the ideal of unobtrusive javascript.
To answer my own question, seems this works and is sufficient for what I need:
link_to "name", nil, :onlick => "alert('Hello, world!')"
link_to_remote can be done like this
link_to "target",:remote => true
and to do a ajax post/get you have
link_to "target", {:controller =>
controller, :action => method, }, :remote => true
In rails 2 it was like this
link_to_remote "target", :url =>
{:controller => controller, :action =>
method, }, :remote
=> true

Javascript on page is not executing before AJAX onComplete event is called

I have a form that makes an Ajax POST request to insert a widget into my database. In the form, I have a select box where you can select from the widgets. After the db insert is made, I must update the select box. I actually just replace the entire form for now.
Because the select box has the widgets, I must have a copy of the objects in javascript. I call this var widget_objects. When the form is replaced during the update event, I print the ruby variable <%= #widget_objects %> and I can see the newly created object. However, when I try to access the javascript var "widget_objects" in the onComplete event, the new object does not exist. I create the javascript widget_objects with this line of code on the page:
widget_objects = <%= #widget_objects %>;
So it seems that the line of code above is not executed before Ajax request's onComplete event. However, I thought the onComplete event occurs after the page has been loaded, and I would assume after scripts are eval'd....any ideas?
<%= submit_to_remote(
"save_widget",
"Save Widget & Generate Embed Code",
{
:url => widgets_url(:user_id => #user.id),
:update => "widget_form",
:method => :POST,
:html => { :id => "save_widget_button",
:onclick => "this.value='Saving...'; this.disabled = 'true';",
:style => "width: 220px;"
},
:complete =>"
$('save_widget_button').disabled='';
$('save_widget_button').value='Save Widget & Generate Embed Code';
var last_id = $j('select#widget_id').children(':last').attr('value');
alert( widget_objects[last_id] );
",
:success => "reportMessage('success', request.headerJSON.success, 'save_widget_status'); $('band_form').reset();",
:failure => "reportMessage('failure', request.headerJSON.errors, 'save_widget_status');"
}) %>
When dealing with these kinds of problems, use Firefox' JS debugger (install the Firebug add-on), enable automatic breaking on exception (from the Scripts tab), and reload the page. If Firefox intercepts an exception (e.g. originating from within the response rjs) then fix the exception. You may also want to just surround your rjs and :complete code with a 'try { ... } catch(e) { alert(e) }' JS wrapper.
Next, use the JS debugger to set breakpoints inside prototype.js's respondToReadyState method, where you'll be able to inspect the rjs reply from your app and step through the evalResponse() method. You'll narrow it down pretty quickly.
I had something similar to this happening in my PHP scripts, where the timing of the scripts seemed out of sync. It was more about how code that appeared AFTER the ajax call would execute before the code in the onSuccess event, and the solution was to wrap the entire event response code inside an anonymous function, like this:
onSuccess: function(reply)
{
resp = reply.reponseText();
pieces = resp.split('|');
(...etc...)
}
You might try something similar after your :complete identifier. Hope this helps!
Setting EvalScripts to true must work.
else you can try loading the script using
> <script> function window.onload(){
> alert('I am loaded as the page
> completes loading') } </script>
if this does not help then you can call function :loading in ajax calls while the response is received the function will be called.

Resources