views:

854

answers:

2

I have a partial for a "guest" object which renders a link to a destroy action like this:

<%= link_to_remote "remove", :url => { :action => "destroy", :id => guest.id } %>

When called from an ERB view it works fine, e.g.

<div id="guests">

<%= render :partial => @event.guests %>

That is, it renders something like:

<a href="#" onclick="new Ajax.Request('/guests/destroy/10', {[options removed]}); return false;">remove</a>

The problem is, when this partial is rendered from RJS as a result of a create operation like this:

page.insert_html :bottom, :guests, :partial => @guest

Instead of linking to the destroy action, it links to the detail/show action for the guest instead, e.g.

a href="#" onclick="new Ajax.Request('/events/1/guests/10', {[options removed]}); return false;">remove</a>

Can anyone tell me what I'm doing wrong, or if there's a way to debug what's going on here? As you might be able to tell, I'm a complete newbie to Rails ;)

+2  A: 

Turns out "destroy" has special meaning in Rails. I renamed the destroy action to delete instead and it works fine.

Luke Sampson
+3  A: 

You were doing it almost right, but you're not quite following convention. Allow me to explain.

Links are designed, by HTTP specification, to be GET requests - data retrieval. You are not supposed to use links to modify a resource's state. Basically, don't use links for potentially destructive actions, as they could be followed by a spider or accelerator. Rails defaults to making every link a GET request. So, when you did this:

<%= link_to_remote "remove", :url => { :action => "destroy", :id => guest.id } %>

It made a GET request. I assume you have in your routes.rb file something like

map.resources :guests

This creates a ton of routes that you could use like this:

<%= link_to_remote "remove", :url => guest_path(guest) %>

With resource routes in Rails, the METHOD used determines the controller action called.

The URL /guests/1 called with a GET request calls the SHOW action. You need to specify the method in your link_to_remote call - either :get, :put, :post, or :delete. :put is for updates, :post is for creates, :delete is for, well, deleting.

<%= link_to_remote "remove", :url => { :action => "destroy", :id => guest.id }, :method => :delete %>

or cleaner,

<%= link_to_remote "remove", :url => guest_path(guest), :method => :destroy %>

But seriously, don't use links. Use buttons.

<%= button_to_remote "remove", :url => guest_path(guest), :method => :destroy %>

Hope that helps!

Brian Hogan
Links are actually a fine approach for deleting or removing data. It's quite common to use buttons for generating or creating data and links for removing or destroying it, specially on tables. Why wouldn't he use them?
Yaraher
Thanks @Brian for the best-practice examples - that helps a lot!I tend to agree with @Yaraher that using links to destructive actions is acceptable in some cases, especially since the action is performed on the onclick so crawlers and pre-loaders wouldn't perform the action accidentally (I think?). But I get your point that it's not the safest thing to do.Thanks again Brian, and Yaraher for your input!
Luke Sampson
@Yaraher: No, links are not a "fine" approach and their common use is not a good excuse. GET requests are designed to retrieve data from a server, not to modify data on the backend.http://ajaxian.com/archives/ruby-on-rails-uses-ajax-to-simulate-post-linksNote that using links with other methods in Rails only works with Javascript enabled which can render the page useless for those with screenreaders who have disabled JS, or mobile users who have no JS support.CSS can fix rendering issues with buttons inside of table cells, and it's trivial to make a button look like a link.
Brian Hogan
Isn't the method (GET vs DELETE in this case) a much, much bigger aspect of the correctness of the approach? Whether it's a link or a button is a presentation issue, not a protocol issue. Setting the method to DELETE will prevent crawlers from following the link. If disabled JS is a concern (and yeah, it should be), my personal opinion is that you should accommodate that using other methods. It would be a shame to let non-JS considerations overly influence the UI presented to JS-capable site visitors.But other than a different opinion about buttons, I thought your answer was right on.
Darryl
@Darryl: It's either a post or a get with Rails though... using :method => :delete still does a post but it only does it via JS. It appears as a GET request to spiders, but also to non-js users, including the blind who choose to disable it, or Blackberry users with horrible JS suppor that turn it off. Unobtrusive proper JS solutions should always win out, if nothing else than for accessibility. Doing it right doesn't hurt the UI, if you *do it right*. It is trivial to style a button or a link however you like if you know CSS well.
Brian Hogan