Skip turbo-frames that are not in the current scope

Hi,
I saw few examples where I’m positioned in the turbo-frame A, and after some action within A navigation scope, I receive rendered HTML with frames A, B, C, D. Content from turbo-frame A is extracted and old content in A is replaced with the new one. B, C, D turbo-frames required DB queries, server-load, bandwidth, they were even rendered and never used. Do you think this could be optimized in one of the future versions?

Is it possible to extend turbo-frame functionality, so it renders only turbo frame that has the same ID as turbo-frame from navigation scope, or from the target attribute? I suppose this could be done via additional

  • HTTP header with turbo_frame_id navigation scope

  • set instance variable based on that header

  • guard clause in turbo_frame_tag based on turbo_frame_id == instance variable

The idea behind this is to respond with turbo_frame that is actually needed, and not with the whole page and multiple turbo-frames.

Additional:

And it might also be helpful to extend the target attribute on turbo_frame_tag so it accepts array of frames = [A, C]. E.g. If A-frame is all messages, and C frame is form for a new message. In that case, we would only refresh all messages and the form. Navigation B would stay untouched.

Thank you, Filip

Seems like you could simply render frame A by itself and target that frame in your navigation. I have learned to break my views up into many more partials than I otherwise would in order to have this kind of flexibility.

The pattern I use is generally like this:

  1. Any chunk of html I want to swap out or update should be wrapped in a turbo-frame.

  2. Any navigation (link or form) that returns html to swap out or update should target the turbo-frame to be changed using the data-turbo-frame attribute.

  3. The response to any navigation must also be wrapped in a turbo frame matching the chunk to be updated or replaced.

  4. For multiple chunks to be updated, use the closest turbo frame that wraps them both and render that entire chunk.

1 Like

I agree up until point 4. What I’ll do is use turbostreams and render each one that I know needs to change for that action and then drop them into an array to send to render turbo-stream:. Then the output from Rails is just the HTML sections that need to change.

Here’s an example of how I structured my controllers on one test project - its not a perfect example for what OP is trying to do, but it shows how: https://gist.github.com/DaveSanders/58d222f81619bc790e804dc94eda0ffb

This way you don’t really worry about finding the nearest frame to re-render. You just don’t put it into the array of streams unless you need it to update.

But yeah, to OP: if you don’t want those sections to update / run DB queries, etc. then control that at the controller - which is where I think that logic should be.

In your additional example, where would that logic be to decide which frames need updated? it would have to be client side logic somewhere, Turbo isn’t going to know which additional frames to update based on the user event on it own. In which case, to me it makes more sense for that logic to live at the controller.

If you really want the logic client side, then you could possibly do it yourself by included a frame id param in the url or post variables back to your controller and let that dictate the logic on which frames to render.

I don’t quite understand how you are able to get turbo_streams to work for normal GET requests that don’t expect a redirect. I have only been able to render turbo_streams for create and update or other form submissions.

In your example, is your edit action called via POST?

Normally I have to render in html format for any non-form submission navigation.

No, edit is a GET in this example. It’s been a little bit, so I don’t remember why I did that one with a stream, but it was probably due to the page layout.

This was part of a multi-tab UI for an admin tool, so when they were flipping tabs, I would build the form for that page from the partial and send it down to replace a frame within the page. I might have also not done this in the best way possible, but this was the pattern that seemed to work well for the tools we were building.

I’m not understanding what you mean by that. TBH, I rarely worry about formats, because I’m not usually building apps where it makes sense to have an API request and a HTML request driven by the same set of rules, or data. Maybe that’s an architecture flaw in how I think, but I like having my page controllers always returning html, and if I need to build an API, I build separate json controllers.

Any time I have tried to render a turbo stream from a get request it hasn’t worked. I will try again tonight as a sanity check, but I was under the impression that rendering turbo streams only works for form submissions.

The docs only give examples in form submission actions such as create: Turbo Handbook

Oh I see. Well, if it were a GET request when there weren’t any turbo frames loaded on the page already (like hitting the url in your browser to load the page the first time) then yes, they wouldn’t work. You just get the text of the markup back in the browser. (which I’ve gotten from time to time experimenting with turbo.)

In my use case, I’ve already loaded my page. These GETs are to retrieve a piece of the page during navigation, and replacing a frame that is already there.