I do not understand how this works at all. I have several questions
Why do we need turbo_frame_tag in the application.html.erb and in new.html.erb. Why can’t we just have it one place? How does that work?
What is actually happening behind the scenes? The browser makes a turbo_stream request and then sends whatever is in new.html.erb sending only that change to the client? I don’t understand how turbo_streams have got anything to do with this.
Turbo at its core is a JavaScript library. It’s just one that makes it so that YOU don’t have to write much JavaScript. Basically every link in your app (unless told otherwise) triggers a turbo request. That request goes to the server and gets a complete html page. Then turbo intercepts the response, pulls out the frame it wants, and replaces the frame with the same name on the existing page with the frame from the response. “The frame that it wants” is the key to answering your question.
By default, there is a frame around your entire page called “_top”. If you never define any frames on the page, this is the frame that gets replaced. Every request in rails is a turbo request. If you define other frames, the link will replace whichever frame is the link’s nearest parent. If you explicitly tell the link to replace a different frame (using data: { turbo_frame: “foo” } or data-turbo-frame=“foo”), then that is the frame that gets replaced.
You need to define the frame in both places so that turbo knows which part of the response needs to be used and which part of the existing page needs to be replaced.
Remember that the turbo_frame_tag is just a helper that generates html. The real thing being used is just an html tag that looks like this: <turbo-frame id=“foo”>…</turbo-frame>
Turbo streams are something related but entirely different from turbo frames. Frames are good if you want to replace one part of a page. If you want to replace several different parts of a page (without just using a larger frame), then turbo streams are useful.
Like I mentioned above, every request gets intercepted by turbo. If turbo finds tags in the response, it handles them. A stream basically tells turbo to find an element with a specific id and replace, append, etc that element with the contents of the stream tag. You don’t need any frames defined to use streams. They just use IDs. (Sorry for the lack of code examples, I’m typing this on my phone)
Thank you! That explains it perfectly. I was also confused about _top since I came across it in someone else’s code but you answered that as well. I had one follow up question regarding turbo_streams:
When a file is called edit.turbo_stream.erb and has the following content like:
<%= turbo_stream.replace "modal-body" do %>
<%= render "form", gradebook: @gradebook, url: "/gradebook/create" %>
<% end %>
Does this mean when a person, say, clicks on a link_to that navigates to the edit path, the browser first makes a turbo request rails, rails responds with nothing since there is not html template but on the way back the response is intercepted by turbo which sees there is an edit file that matches the path being called ( edit.turbo_stream.erb) which then renders that file?
Then the code turbo_stream.replace simply finds the HTML element with id “modal-body” and replaces it with what is inside the block?
You’re close, basically rails treats a *.turbo_stream.erbnearly the same as *.html.erb. When a request is made to a controller action rails looks for [action].html.erb and then [action].turbo_stream.erb. I’m a little fuzzy on the details here but if I understand correctly if it uses the .turbo_stream.erb file it sends a special header that tells turbo to intercept the request and execute the stream directives.
Basically, Rails respond with text/vnd.turbo-stream.html content type which causes Turbo to “activate” the stream action tags. This actually just means that it inserts the tags into the DOM tree and lets the browser evaluate it as a custom element.