views:

54

answers:

2

Hey everyone, I'm working on an application and I'm having difficulty with the new Rails 3 link_to. Here's the situation - I have two divs on my "home" page, and each div is populated at document.load with ajax. This works as expected.

In the content of the pages I'm loading into these divs, I want to use the link_to... :remote => true functionality. The source renders as expected with the data-remote="true" tag, but when I click on the links, they're completely ignored (the link is treated as a regular hyperlink).

I've written .rjs files that handle everything properly (because they work when hard-coded), so that's not the issue.

Here's the html source for the ajax-loaded content:

<div>
  <a href="/cart/add/2" data-remote="true">Link A</a>
  <a href="/cart/add/1" data-remote="true">Link B</a> 
</div>

Clicking on one of those links when normally embedded into the page causes my shopping cart to update correctly... however, when I dynamically load this content into the page with AJAX, the link just follows and ignores the data-remote="true"...

Originally I thought it had something to do with prototype not loading correctly in the AJAX stuff, but I've checked all of that and it doesn't change a thing...

I'm really confused... Does anybody have a thought on this?

# add method of controller
def add
  product = Product.find_by_id(params[:product_id])
  params[:quantity] ||= 1
  params[:price] ||= product.price

  unless product.nil?
    @line = session[:cart].purchase_lines.select {|l| l.product_id == product.id}.first
    unless @line.nil?
      @line.quantity = (@line.quantity + params[:quantity].to_i)
    else # create a new purchase_line in the cart
      @line = session[:cart].purchase_lines.build({ :product_id => product.id, :price => params[:price], :quantity => params[:quantity].to_i })
    end
  else
    flash[:error] = "Unable to add the selected product"
  end
end  

and my rjs

# add.rjs
page.if page['product_' << @line.product_id.to_s] do
  page.replace 'product_' << @line.product_id.to_s, :partial => 'line'
page.else
  page.insert_html :bottom, 'cart', :partial => 'line'
end

page.replace_html 'sub_total', number_to_currency(session[:cart].sub_total, :unit => '$')
page.replace_html 'tax', number_to_currency(session[:cart].tax, :unit => '$')
page.replace_html 'total', number_to_currency(session[:cart].sub_total+session[:cart].tax, :unit => '$')
page.visual_effect :highlight, 'product_' << @line.product_id.to_s, :duration => 1
+1  A: 

I'm pretty sure the problem here is that when your page loads the first time, the Rails UJS javascript library you're using (Prototype by default, or jQuery, etc.) binds a listener to the onclick event of the anchor (link) element. However, when you update your page with new markup dynamically (via ajax in this case) the new anchor tags do not receive this binding because the UJS library only does it's thing when the page first loads. If you're using jQuery as your UJS you should look into the jQuery.live() event handler attachment located in the jQuery API docs

The description from the API is as follows:

Description: Attach a handler to the event for all elements which match the current selector, now and in the future.

Specifically you should give the div that will receive the AJAX loaded content an id let's say "cart-links", then add some javascript to the page similar to the following:

$('#cart-links a').live('click', function() {
  // make your AJAX calls here
});

I'm not sure if there's a Prototype equivalent if that's what you're using.

The further I've gotten into javascript and Rails, the more I realize that a lot of the Rails javascript helpers actually make javascript development and AJAX handling more difficult, and I tend to just use raw jQuery.

Good luck!

Patrick Klingemann