What is the best way to navigate back to a site in rails and restore the state of the page?
I know, i can do
<%= link_to "Back", :back %>
But is this a good approach?
My need is, that I want to restore the state of a page, after navigated forth and back. E.g. I use a param to paginate data on a page, navigate to another page, to edit something and navigate back. The param should be restored, so I don’t lose the view.
How are you doing that editing? If traditional form post, then :back won’t do what you want as the previous page was still that detail page. Even if you are editing that detail page via fetch or XHR or something :back is still unreliable. It uses the referrer which may not be set, falls back on javascript which if you came from a new tab or something may also not be reliable.
IMHO the :back option is a cool trick for something quick but there are just some corner cases it doesn’t handle well. One option I have used before is to save my index state (current filter, current page, current sort) in the session. If that info is not provided via param, fallback to the session. Anytime it is provided, store it in the session. This way you can always link to the index and the last state is restored. The only downside is it doesn’t play well with HTTP caching (although you could always redirect using the session info if you wanted).
Another option is to not navigate away to do the editing. Edit inline. The Turbo Frames stuff is very useful for this.
I understand all your concerns about :back and I also thought about it. :back will not show you the updated data after navigating back and if you visited the edit-page from a bookmark, the back.button would not bring you back to the index-page.
Thank you for the hint with the session. I think this is a good idea instead of storing the data in the params. You wrote:
The only downside is it doesn’t play well with HTTP caching (although you could always redirect using the session info if you wanted).
I don’t understand this, could you explain this a little bit?
Your other option about Turbo Frames also sounds interesting, but I have first to understand it. Maybe this is the best solution.
So if your controller action looks something like this:
def index
@widgets = Widgets.search params[:query]
fresh_when @widgets
end
Note the HTTP caching used. If someone requests /widgets vs /widgets?search=foo the HTTP caching is not an issue since they are different requests. Now let’s say you want to persist the search as you move to the detail page and when you link back to the search page your previous search is already in place. You might do something like:
def index
params[:query] ||= session[:widgets_query]
session[:widgets_query] = params[:query]
@widgets = Widgets.search params[:query]
end
Note that I dropped the HTTP caching. Now when my detail page can just do:
<%= link_to 'Back to Search', widgets_path %>
When they go back to the index page it sees there is no active search but a previous search in the session so it uses that as the active search. The problem is if we try to add our fresh_when @widgets line back we now have /widgets which we want to search for “foo” but it will just use the cached version that wasn’t searching for foo. We might be able to fix this by doing a redirect:
def index
redirect_to widgets_path(query: session[:widgets_query]) and return if restore_query?
session[:widgets_query] = params[:query]
@widgets = Widgets.search params[:query]
fresh_when @widgets
end
private
def restore_query? = !params[:query] && session[:widgets_query]
I think this would avoid the caching issue while still allowing you detail page to just link to “/widgets”. If there is no session query it would just render all widgets (possibly using HTTP cache). If there is a session query it redirects to a URI that has that query.
NOTE: None of this code is actually tested. Just off the top of my head based on doing something similar many years ago.