Search code examples
ruby-on-railshttp-getturbolinkshotwire-railshotwire

Turbo Stream link_to strips out custom attributes in GET request


I'm using Rails 7. I wanted to create a link using link_to that inserts some custom form fields for a many-to-many relation into a form via a hotwired frame elsewhere on the page. In order to customize the form fields generated (setting defaults etc) I need to include some custom parameters in the request:

<%= link_to "Add crew to ship",
      new_crew_path(ship_id: ship.id),
      data: { turbo_method: :get,
              turbo_stream: true } %>

The HTML looks correct, but when clicking the link, the parameter seems to have been stripped away:

<a data-turbo-method="get" data-turbo-stream="true" href="/crews/new?ship_id=1">Add crew to ship</a>
...
Started GET "/crews/new" for 127.0.0.1 at 2023-01-24 13:49:23 +0100
Processing by CrewsController#new as TURBO_STREAM

If I remove the data map defining turbo_stream: true so it becomes a regular request, it works fine:

<%= link_to "Add crew to ship",
      new_crew_path(ship_id: ship.id) %> 

Started GET "/crews/new?ship_id=1" for 127.0.0.1 at 2023-01-24 13:53:26 +0100
Processing by CrewsController#new as HTML
Incoming parameters: {"ship_id"=>"1"}

Same for changing it to a POST request, so it goes to the #create action of the controller instead, that also works:

<%= link_to "Add crew to ship",
            crews_path(ship_id: ship.id),
            data: { turbo_method: :post,
                    turbo_stream: true }  %>

Started POST "/crews?ship_id=1" for 127.0.0.1 at 2023-01-24 13:58:01 +0100
Processing by CrewsController#create as TURBO_STREAM
Incoming parameters: {"ship_id"=>"1"}

So I have workarounds. Nevertheless, out of curiousity, is there some way to make GET turbo requests submit the custom params? From a REST perspective it feels like the most correct request method.


Solution

  • When you add turbo_method: :get the link is treated as a form and GET forms overwrite url query params with form params. As a test:

    // app/javascript/application.js
    
    document.addEventListener("turbo:before-fetch-request", (event) => {
      console.log(event.target)
    })
    // you get this "fake" form in the console, that Turbo submits instead of a link:
    // <form data-turbo="true" action="http://localhost:3000/?ship_id=1" hidden="" method="get" data-turbo-stream=""></form>
    

    If you just change the link to this:

    <%= link_to "Add crew to ship",
      new_crew_path(ship_id: ship.id),
      data: { turbo_stream: true } %>
    

    Now it's a regular GET turbo stream link, and query params should stay untouched.


    For some extra details:
    https://github.com/hotwired/turbo/pull/647

    it's worth emphasizing that declaring [data-turbo-method="get"] on an <a> element is redundant, and should be avoided

    https://github.com/hotwired/turbo/pull/821