Rails remote link_to and access to trigger element in javascript response - ruby-on-rails

in my Rails3 application I have table with rows, each containig link_to with :remote => true
...
<tr><td><%= link_to "remote call", action_controller_path(data), :remote => true %>
...
As response to ajax call I return javascript to execute in browser:
# action.js.erb
console.log(this); <-- "this" is browser's window object not my element
$(this).closest("tr")....
The problem is, that in my javascript I need access to element, which triggered the ajax call (the <a> tag). Is there any way how to get access to it?

I found this answer to a similar problem here.
My solution was to bind a pre-submit class to the element, in my case
a popup modal window. It's a similar solution to the post linked to
above in that it uses the pre-submit bindings, but tailored to use
classes instead.
In public/javascripts/application.rb:
jQuery(function($) {
$(".poppable").bind("ajax:loading", function() { $(this).addClass("popped"); });
});
Then in my view for the popup content (e.g.
app/views/mymodel/popup.js.erb):
var p = $(".poppable.popped");
p.removeClass("popped");
/* Do what I need to with p ... */
If this doesn't look kosher, I'm all ears but it works for now.

Related

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'

Ruby/Rails/AJAX/JQuery - On link click, passing a parameter and performing a controller action

I'm trying to use clicking a link to perform and action in my controller called 'yes' but do so client side rather than having to refresh everytime a user clicks.
Before I had an link_to that routed to a action called "yes" and passed the id of a model I have called 'event'
<%= link_to "yes", yes_path(event)%> (in view)
match 'user/:id/yes' => 'user#yes', :as => "yes" {in routes.rb)
The problem issue is that every time the user clicks the link the page refreshes while it performs the yes action, so it will flow alot smoother if I can tell the backend to perform the actions client side.
S0 I found a reference here : execute controller/action from javascript in rails3
and took a look at the documentation : http://api.jquery.com/jQuery.ajax/
And came up with this. Where if the post is successful at the previous route from above change a css class for the link (change color).
$.ajax({
type: "POST",
url: "/user/" + $(this).attr('event') + "/yes/",
success: function(){
$(".like").click(function() {
if ($(this).hasClass("selected")) {
$(this).addClass("selected");
return false; }
});
I also added this is the bottom of the controller that the desired javascript is being used.
respond_to do |format|
format.html { }
format.js
end
So now my link_to looks like this
<%= link_to "yes", yes_path(event), :class => "like", :remote => true %>
But the page is still refreshing and It doesnt look like its calling the AJAX at all.
am I passing the parameter "event" properly as a jquery attribute?
am I calling the link_to properly?
This is my first time so I have no idea what I'm doing wrong, possibly a few things?
I'd really appreciate any help
Is this what you're after?
$(".like").click(function(evt) {
evt.preventDefault();
var $self = $(this);
$.post($self.attr("href"), function(response) {
$self.addClass("selected");
});
});
The first line binds the JavaScript to all elements with a class of like. preventDefault is the preferred way to prevent the default behavior of an anchor tag (navigate to the href). $.post() is shorthand for $.ajax({ type: "POST" }).
Whatever you want to happen after a successful post to the server goes that finally function call. The first argument is the response from the server.
Rich

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

<%= 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.

: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

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