14

Problem appears when I want to remove element from paginated table in "ajaxized" way. My tasks controller calls its destroy method in response to [DELETE] /tasks/1234, but at the end I want to redirect to index to get the list automatically refreshed.

Unfortunately, what redirect_to tasks_url at this point does is [DELETE] /tasks request.

Is there any way to force GET request instead of DELETE while redirecting from inside of destroy ?

Michal
  • 677
  • 6
  • 16
  • Can you provide the information from your logfile for that redirection request? – arnep Apr 12 '12 at 11:25
  • Sure, here it is: [gist](https://gist.github.com/2369726) – Michal Apr 13 '12 at 06:46
  • 1
    Note: I have the exact same symptoms as described and listed in the gist above. An additional data point: my Chrome browser says it is issuing a `GET`, not a `DELETE`, on the redirect. However the Rails logs show the redirect call is with `DELETE` just like in the gist above. – SingleShot May 13 '12 at 02:48
  • @Michal - please paste your comment that I upvoted into an answer and I will award you the 100 reputation ;-) – SingleShot May 17 '12 at 16:15

6 Answers6

24

Use redirect_to with status 303

def destroy
  @task = Task.find(params[:id])
  @task.destroy
  redirect_to :action => :index, status: 303     
end

redirect_to documentation says:

http://api.rubyonrails.org/classes/ActionController/Redirecting.html

If you are using XHR requests other than GET or POST and redirecting after the request then some browsers will follow the redirect using the original request method. This may lead to undesirable behavior such as a double DELETE. To work around this you can return a 303 See Other status code which will be followed using a GET request.

vitor.caetano
  • 249
  • 2
  • 4
3

Don't use redirect. Use render. Abstract the functionality to get the table data into a library module and then call that from both index as well as the delete methods, and then add then add the render call to the delete method so that the index view is what is sent back in response.

require 'myLibrary'
include myModule

def index
    getTableData
end

def destroy
    flash.now[:notice] = "Delete operation failed" unless Task.destroy(params[:id])
    getTableData
    render 'myController/index'
end

in lib/myLibrary

module myModule
    def getTableData
        # Table Foo here
    end
end
Reuben
  • 53
  • 5
  • Thanks - doing this updates the list of items properly. However, my `will_paginate` links after doing this say `/tasks/2?page=2` (which is wrong), for example, instead of `/tasks?page=2`. This only happens after the ajaxed destroy as you described above. Thoughts? – SingleShot May 13 '12 at 03:46
  • Note, I switched from `will_paginate` to `kanimari` to see if the bad url problem goes away. Negative. – SingleShot May 13 '12 at 05:29
  • Sorry, but there's not way to answer that part without knowing how your app is put together. If you are trying to do this via XHR, you will need to change how the table data is loaded. i.e. This will not work if index loads a complete web page, but the destroy is only a part of a page to be swapped out via an ajax call. – Reuben May 14 '12 at 03:03
  • You will probably want to make it so that index just loads the web page with an empty div with is the target for your ajax calls. Then after the document is loaded, call something like ajax_index to load the table data via XHR. Then ajax calls such as destroy render ajax_index rather than index. That way all ajax calls use the same type of layout that is designed for a partial chunk of a page rather than a complete page. – Reuben May 14 '12 at 03:14
  • 1
    This answer looks the most logical for me, and that's also what I tried to do at the very beginning. unfortunately for some reasons I got exactly the same problems as @SingleShot (kaminari pager rendered weird links). I ended up with hack rather than an elegant solution - I have added `delete "tasks" => "tasks#index", :via => :get` to routes.rb (and used `redirect_to tasks_url` in destroy method) which solved problem I described above and the problem with pager links described later by @SingleShot. – Michal May 14 '12 at 21:24
  • +1 @Michal - Thanks! I was dead in the water on this task without your solution. I don't think its too bad of a hack. I am somewhat new to rails, but feel this is a workaround to a bug in rails, but perhaps that's just me being a noob :-) – SingleShot May 15 '12 at 05:05
1

Ok, so to sum up this question. The easiest way I found to fix the issue, is to mess a bit in routes.rb by adding: "tasks" => "tasks#index", :via => :get and to use redirect_to tasks_url (in my case) directly in destroy action in controller.

This will solve the problem with kaminari pager as well (which renders strange links in some cases).

Michal
  • 677
  • 6
  • 16
0

I think you should rearrange your code on the client side:

  1. Memorize current page
  2. fire destroy action, await true or false
  3. request index at memorized page using ajax call.
flooooo
  • 709
  • 4
  • 14
  • Interesting - basically "do the redirecting yourself". I ended up not rearranging code as you suggested and instead used Michal's suggestion of a special route. Thanks! – SingleShot May 15 '12 at 05:10
0

Why not to use :action param ?

def destroy
  @task = Task.find(params[:id])
  @task.destroy
  redirect_to :action => :index    
end
melekes
  • 1,880
  • 2
  • 24
  • 30
  • This isn't much different from what we were already doing. The redirect will be received by the browser, the browser will issue a `GET` on `index` but rails will use `DELETE` instead of `GET`. I believe it is a bug in Rails myself. – SingleShot May 17 '12 at 23:26
  • Sorry, I think you are right. Unfortunately, Rails do not have Symfony's forward() method (`These methods forward the current request to another action without a round-trip with the browser.`), so it is worth either to modify the routing or use the render method. – melekes May 18 '12 at 07:28
0

You probably don't want to do a standard rails redirect when using ajax. This blog post has some good insight on the issue and solutions. Note this is from a similar question in January, answered by DGM

Community
  • 1
  • 1
Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70
  • Interesting. Contrary to what the similar question describes, a proper redirect is actually occurring in my case - automatically by the browser. The difference is that rails is failing to handle it properly. I solved the problem via a special route as suggested by one of Michal's comments. – SingleShot May 18 '12 at 22:14