Deleting Records with Ajax in Rails
I have a user page where the user can type and submit posts. I'm using Ajax to allow the user to do this from the user/show page. The problem that I'm having occurs when I try to let the user delete posts. When the user clicks the "remove" link, the post is removed from the page. However, when I reload the page, the post is back. It's not getting destroyed.
I have the same code implemented on the user/edit page in a different context, and that works fine because the post record is destroyed when the form is submitted. However, in this situation where there is no form submitted, I'm not sure how to get this working.
Here's my setup:
:user has_many :posts
:post belongs_to :user
:post belongs_to :post_type
:post_type has_many :posts
User#show:
This is the code for the user to submit the post. This works perfectly.<% if logged_in? && current_user == @user %>
<% form_tag(:controller => "posts", :action => "create") do %>
<div><%= text_area_tag(:message, nil, :size => "27x3") %></div>
<div><%= submit_tag("Post") %></div>
<div><%= label_tag(:post_type_id, "Update type?", :class => 'typeLabel') %></div>
<div><%= collection_select(@posts, :post_type_id, PostType.all, :id, :name) %></div>
<% end %>
<% end %>
This is the code where the posts are displayed, including the "remove link" at issue:
<% @user.posts.each do |c| %>
<div class="postS">
<div><%=h c.message %></div>
<p><%=h c.post_type.name %></p>
<p>Posted <%=h time_ago_in_words(c.created_at) %> ago</p>
<% if logged_in? && current_user == @user %>
<%=开发者_StackOverflow社区 link_to_function "- Delete Post", "if(confirm('Are you sure you want to permanently delete this post?')) $(this).up('.postS').remove()" %>
<% end %>
<%end%>
PostsController
def destroy
@post = Post.find(params[:id])
if @post.destroy
redirect_to :back
else
flash[:notice] = "Post failed to delete."
redirect_to :back
end
end
Let me know if any other information would be helpful. Thanks!
Update 2
I tried using link_to_remote
as explained in the answer below. I agree this is the right solution, but can't get it working. I think it's because of a routes issue.
I have posts set up as a user resource like this:
map.resources :users, :member => { :enable => :put } do |users|
users.resources :posts
end
When I implement the link_to_remote
code provided in the answer, I get a "no method error" exception as follows: undefined method
post_url' for #`
I tried some tweaks that accounted for this, but none of my attempts worked. Any ideas? Am I right that this is related to my route?
UPDATE
The code from the Rails Guides is not working. I tried many variations as well.
The closest I've gotten is this:
<%= link_to_remote "- Delete Post", :url => { :controller => 'posts', :action => 'destroy', :id => @user.posts, :method => :delete }, :confirm => "Are you sure you want to permanetly delete this post?", :success => "$(this).up('.postS').remove();" %>
Using this code, I click the delete link and I am prompted to accept the deletion. After I accept, the post is not deleted on the screen. HOWEVER, it is deleted from the database. Then, when I reload the page, the post is in fact deleted from the screen. So this is mostly working, except that the ajax removal action is now not functioning.
But the code seems wrong. Notice that I wrote: :id => @user.posts
This doesn't seem right to me, but it's the only way I got this code to work at all. Look what this does to the log file:
Processing PostsController#destroy (for 127.0.0.1 at 2009-12-17 02:07:05) [POST]`Parameters: {"action"=>"destroy", "authenticity_token"=>"GZfPHUc2+qPa6Fj5jNogMyhuB4YUO+KlOdkwjM5NSvw=", "id"=>"12/11", "method"=>"delete", "controller"=>"posts"} `
Notice the id=>"12/11". That's not what I want happening.
When I change the id paramater to :id => @user.post, I get a "no method error." undefined method `post' for #User:0x3ddc20c
When I try :id => user_post_url(@user, @post)
, I get: "user_post_url
failed to generate from [and then it lists all the database column values for this user].
When I try :id => user_posts_url(@user, @posts)
, the page loads. When I click the delete link I'm prompted to accept. Then nothing happens. When I reload the page, the post is still there and never deleted from the database.
That's every variation that I could think of. Any ideas?
Update 3
This is what my routes look like:
user_posts GET /users/:user_id/posts(.:format) {:controller=>"posts", :action=>"index"}
POST /users/:user_id/posts(.:format) {:controller=>"posts", :action=>"create"}
new_user_post GET /users/:user_id/posts/new(.:format) {:controller=>"posts", :action=>"new"}
edit_user_post GET /users/:user_id/posts/:id/edit(.:format) {:controller=>"posts", :action=>"edit"}
user_post GET /users/:user_id/posts/:id(.:format) {:controller=>"posts", :action=>"show"}
PUT /users/:user_id/posts/:id(.:format) {:controller=>"posts", :action=>"update"}
DELETE /users/:user_id/posts/:id(.:format) {:controller=>"posts", :action=>"destroy"}
UPDATE 4
Based on the guidance from ScottD below, my code now looks like this:
<%= link_to_remote "- Delete Post", :url => user_post_url(@user,c), :method => :delete, :confirm => "Are you sure you want to permanently delete this post?", :success => "$(this).up('.postS').remove();" %>
This solves most of my problem, but the javascript remove action is still not getting triggered. In other words, I click the delete link. The post is deleted from the database, but remains on the screen. When I reload the page, the post is gone. So, the problem is that the following part is not getting triggered: :success => "$(this).up('.postS').remove();"
Here's what happens in the log when I click on the delete link as described directly above:
Processing PostsController#destroy (for 127.0.0.1 at 2009-12-17 11:59:18) [DELETE]
Parameters: {"action"=>"destroy", "authenticity_token"=>"ccF1QNCGs9IlvbIYWwmQmcbi7kROgQsTZjM1dM687xA=", "_method"=>"delete", "id"=>"14", "controller"=>"posts", "user_id"=>"1"}
User Columns (3.2ms) SHOW FIELDS FROM `users`
User Load (29.6ms) SELECT * FROM `users` WHERE (`users`.`id` = 1) ORDER BY name
Post Columns (8.7ms) SHOW FIELDS FROM `posts`
Post Load (1.6ms) SELECT * FROM `posts` WHERE (`posts`.`id` = 14)
app/controllers/posts_controller.rb:22:in `destroy'
SQL (0.1ms) BEGIN
app/controllers/posts_controller.rb:23:in `destroy'
Post Destroy (0.5ms) DELETE FROM `posts` WHERE `id` = 14
app/controllers/posts_controller.rb:23:in `destroy'
SQL (0.9ms) COMMIT
app/controllers/posts_controller.rb:23:in `destroy'
Redirected to http://localhost:3000/users/1
Completed in 65ms (DB: 45) | 302 Found [http://localhost/users/1/posts/14]
To me, this all looks good. So I'm not sure why the javascript removal is not working.
When I reload the page, the log shows everything loading normally and the post is gone (as it should be). Any ideas on how I can get the javascript removal to work?
Update #5
Here's what's currently generated in the html based on the link_to_remote
code that I'm using:
<a href="#" onclick="if (confirm('Are you sure you want to permanently delete this post?')) { new Ajax.Request('http://localhost:3000/users/1/posts/18', {asynchronous:true, evalScripts:true, method:'delete', onComplete:function(request){$(this).up('.postS').remove();}, parameters:'authenticity_token=' + encodeURIComponent('ccF1QNCGs9IlvbIYWwmQmcbi7kROgQsTZjM1dM687xA=')}); }; return false;">- Delete Post</a>
Here's what Firebug shows in the console when I click the "delete" text on the page:
POST http://localhost:3000/users/1/posts/18 302 Moved Temporarily
GET http://localhost:3000/users/1 200 OK
The problem is that you are using link_to_function
which only calls a Javascript function, it does not POST anything to your app. If you want to do Ajax try link_to_remote
. That will make an AJAX call, and then it lets you call javascript based on callback hooks like :success
or :complete
.
Example based on your sample:
<% if logged_in? && current_user == @user %>
<%= link_to_remote "- Delete Post", :url => user_post_url(@user,c, :format => :json), :method => :delete , :confirm => "Are you sure you want to permanetly delete this post?", :success => "$(this).up('.postS').remove();" %>
<% end %>
Check out the Rails API docs for link_to_remote
for more examples.
You will also need to update your Post Controller to respond to the delete request from Ajax. I use the new json gem to render the json.
def destroy
@post = Post.find(params[:id])
respond_to do |format|
if @post.destroy
format.html { redirect_to :back }
format.json { :success => true }.as_json
else
flash[:notice] = "Post failed to delete."
format.html { redirect_to :back }
format.json { :success => false }.as_json
end
end
end
Updated to reflect nested routes in question.
In case it helps you I have similar problem now in my application and until now I have tracked it down to the fact, that everything except the
:success => "$(this).up('.postS').remove();"
part of the link_to_remote did not work the way imagine. For some reason it does not find the right div in the DOM.
I am not claiming that it is your problem, but it might get you on the right track.
精彩评论