Search code examples
ruby-on-railsrouteslink-to

Go back to path from show view based on render


This is confusing me so much, I have 2 models client has_many :invocies and invoice belongs_to client, I have my root set up to index all invoices and invoices can also be accessed through the clients show View. I have a back button in my invoice show view that links back to the client:id that the invoice belongs to

Back link form invoices/show

<%= link_to 'Back', client_path(@invoice.client), class: "btn btn-primary" %>

This works fine but its really awkward to launch invoice show from the root path and then click back and be re-directed to the client show view. Is there a way to go back to the root_path or show 2 buttons in an if statement and show one depending on how a user launched the invoice show view in the first place? Completely stuck.

Here is my routes file

resources :invoices, only: [:new, :create, :destroy, :index]
 resources :clients do
   resources :invoices, shallow: true
end

root 'invoices#index'

Solution

  • A simple backlink:

    link_to 'Back', :back, class: "btn btn-primary" 
    

    should do in this case. It will use the HTTP_REFERER or a Javascript function to get to the previous page. See the documentation for more info.

    Update:

    I you need some exceptions to the "simple back link" rule, you must create a custom solution. One such solution might be that you use link_to :back by default but override it by accepting a custom "back_path" parameter in special cases. You can create a custom helper to do that.

    The following example uses the standard back link by default but allows the previous page to set a back_path parameter that will be understood by the back_link helper.

    # application_helper.rb
    def back_link(params, name: "Back", class: "btn btn-primary")
      if params[:back_path].present?
        link_to name, params[:back_path], class: class 
      else
        link_to name, :back, class: class 
      ens
    end
    

    Now you use the link normally in your views:

    <%= back_link(params) %>
    

    And when you want to override the default behavior, simply pass the back_path param. You will pass this parameter typically at the page that you want to return to. Then you'll have to pass this parameter on through all further pages and/or redirects.

    Say, for a non-trivial example, that from the client show page you can click a link to create a new invoice and then be redirected to the invoice show page and from there you want to click the back link and get to the client show page again.

    So, add a link to the clients show page, with the back_path param pointing back to this page:

    # views/clients/show.html.erb
    <%= link_to 'New invoice', new_invoice_path(@client, back_path: client_path(@client)) %>
    

    In the new page form, you'll have to add a hidden_tagto pass on the back_path param:

    # views/invoices/_form.html.erb
    <%= hidden_field_tag 'back_path', params[:back_path] %>
    

    Next, in the controller's create action, you will also have to pass this param upon redirect:

    # controllers/invoices_controller.rb
    redirect_to invoice_url(@invoice, back_path: params[:back_path])
    

    And finally, you can now use the back_link helper to display the correct link:

    # views/invoices/show.html.erb
    <%= back_link(params) %>
    

    I know it's ugly but that's the way it is... Moreover it's not an ideal solution as typically you want a hierarchy of back links as you browse the pages (and come to a page from different paths) and this solution solves only the first back link. But I think it's something to build upon.