开发者

rails link_to :remote from within ajax-loaded content

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 tha开发者_如何学Ct 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


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!


I agree with Patrick, you are probably binding when the DOM is loaded so any newly added elements don't get bound. If the javascript you're using (i.e. Prototype) doesn't have functionality like jQuery's live(), you can encapsulate the bind logic in a function which unbinds and then rebinds itself (you may want to namespace the event so you don't unbind any other handlers, but I'm not sure whether or not Prototype supports namespacing events), and then you can call that function when the new element is inserted. Alternatively, you can isolate your binding to the newly created element instead of binding to every element with that class name or tag name.

And like Patrick, I also prefer to use raw jQuery. This way you don't have to learn what rails.js does and hack in workarounds for scenarios where it doesn't do what you want. As a developer, you should know what you want your javascript to do, and do it. If you prefer to use Prototype, you should still make your own rails.js, but really jQuery is a lot nicer in my opinion.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜