views:

290

answers:

3

As you know, when you upgrade from Rails 2 to 3 you replace this:

link_to_remote "more", :url => {...}

with this:

link_to "more", {...}, :remote => true

But how do you handle the :update option in link_to_remote? In Railscast #205 Ryan Bates demonstrates link_to with :remote and a server response that includes JavaScript code to update a particular element in a page, but this practice seems wrong to me. I want my server's response to be a simple HTML fragment which is easy to test and which can be used by different pages (clients) in different ways. I don't think the server should have to know the ID of the target element on the requesting page as it ties the action to the page (a mini-client) and therefore makes it less general (it also feels uglier than a pure HTML response).

So, to be explicit, is there a way to do something like this:

link_to_remote "more", :url => {...}, :update => "products-list"

with Rails 3 and UJS? Or do I have to write JavaScript to capture the server's HTML response and insert it into the right element on the page?

If the latter, please describe the best approach (can link_to's :remote option be used at all?).

A: 

in the js view /apps/views/product/index.js.erb you can write JS code such as the following

prototype:

$("products-list").update("<%= escape_javascript(render(@products))%>");

jquery:

$("products-list").html("<%= escape_javascript(render(@products))%>");

or

$("products-list").append("<%= escape_javascript(render(@products))%>");

and it will be executed when the request is successful

ErsatzRyan
This is exactly what I said I don't want to do, because it ties the server's response to an element on the page ("products-list"). That prevents me from using the same URL to update another page where I might want to insert the same content into an element with a different ID.
Alex Reisner
so pass the id of the element you want updated to the controller and have it use that in the view
ErsatzRyan
A: 

yes, you can apply the latter approach (write custom JS to get server response) by using *link_to :remote*.

you can also choose to get a json response, then update data on the page using JS. in any case, remember to render only the partial, not the entire page.

EDIT:

a sample code, this should make AJAX call when clicking something with id "more", then update a #product-list element in the page:

 $("#more").click(function() {
 // make a POST call and replace the content
    $.post(, function(data) {
      $("#products-list").html(data);
    });
  });

you don't need more code, and you can write some helper to generate this JS, instead of writing code in the view. btw this is yet UJS

apeacox
Can you give some sample code? If I use the `:remote => true` option, how can I capture the server's HTML response and insert it into the page?
Alex Reisner
updated my reply
apeacox
Thanks for the code, but that looks like something I'd use instead of `:remote => true`, or am I missing something? Doesn't `:remote => true` handle the POST/GET request? What I need is a way to intercept the server's response from outside the code that initiates the request (which is in the Rails UJS Prototype or jQuery "driver"). Does this make sense?
Alex Reisner
+1  A: 

I'm not 100% sure that this is the Rails-endorsed way to do it, but I've found a solution that works and seems pretty clean. Let's say we have a page that lists the ten most popular products in our database. At the end is a link to load all remaining products via AJAX:

<ul id="products-list">
  <li>...</li>
  <li>...</li>
  <li>...</li>
  ...
</ul>
<%= link_to "more...", "/products/all", :id => "load-more", :remote => true %>

The server returns plain HTML (so that the link can be used on many different pages and is not bound to particular DOM IDs). We use the ajax:x events triggered by the Rails UJS drivers (here I'm using jQuery) to grab the server's response and insert it into the right element on the page:

<%= javascript_tag do %>
  $("#load-more").bind("ajax:complete", function(et, e){
    $("#products-list").html(e.responseText);
  });
<% end %>

If desired, we can also use the ajax:loading event to show a "spinner":

<%= javascript_tag do %>
  $("load-more").bind("ajax:loading", function(et, e){
    $(this).hide();
    $("#products-spinner").show();
  });
<% end %>

The Rails UJS drivers trigger four other events as well: ajax:success, ajax:failure, ajax:before, and ajax:after. See the driver included in your app (public/javascripts/rails.js) for more information.

Alex Reisner