How do you use a turbo frame to replace an object creation form with an object show partial?

Apologies if this is rudimentary, but I’ve spent hours watching tutorials and reading documentation and still can’t figure this out.

I have a model subscription_tier with show and edit actions with corresponding views. I’ve wrapped each of these with a turbo frame

<%= turbo_frame_tag subscription_tier do %>

When I edit an existing subscription tier and save it, the turbo frame refreshes and shows my saved tier, but I’m unable to replicate this for creating new tiers.

My new tier frame:

<turbo-frame id="new_tier">
      <%= link_to "Add Tier", new_create_subscription_tier_path(sub_type: "Free"), class: "btn btn-primary mb-3 fs-6"
      %>
 </turbo-frame>

and in new.html.erb


    <turbo-frame id="new_tier">
      <%= render partial: "create/subscription_tiers/edit", locals: {
        subscription_tier: @subscription_tier
      } %>
    </turbo-frame>

Clicking Add Tier button successfully renders the form and I can save the object, but on save the turbo frame is destroyed with the error Response has no matching <turbo-frame id="new_tier"> element

I know this is because my show partial is wrapped with the <%= turbo_frame_tag subscription_tier do %>, but I don’t know how to reconcile this. I’ve tried using “_top” to break out of the frame, but the error still occurs.

Does the action that create the tier on the server return a matching turbo-frame with the name new_tier?.

class TireController < ApplicationController
  def create
     # create the record
  end
end

tire/create.html.erb

<turbo-frame id="new_tier">
  # render new tire record
 </turbo-frame>

No, after creation it redirects to show which is wrapped in turbo-frame id="#{tier_id}"

is the solution then to have two different “show” views, one that wraps the tier with a new_tier and one that wraps in the existing id? if so, it will break the functionality of editing the tier, where I have an Edit button that renders a _form partial that also wraps with turbo-frame id="#{tier_id}"

Is there a way to tell Turbo to replace the current new_tier frame with a frame with a different id?

Ah, I think the puzzle places might be starting to fall in place for me but let me know if I’m overthinking it. If I add a turbo stream below my new_tier frame, I can have the create action add to that stream? something like

<turbo-frame id="new_tier">
      <%= link_to "Add Tier", new_create_subscription_tier_path(sub_type: "Free"), class: "btn btn-primary mb-3 fs-6"
      %>
 </turbo-frame>

<turbo-stream action="replace" target="tier_1">
  <template>
    <div id="tier_1">
      #render partial show
    </div>
  </template>
</turbo-stream>
class TierController < ApplicationController
  def create
     # create the record

    if @tier.save
        format.html { redirect_to @tier}
        format.turbo_stream
  end
end
# app/views/tiers/create.turbo_stream.erb
<%= turbo_stream.replace "tier_1" do %>
  <%= render partial: "show" %>
<% end %>

This is documented in the docs. See here.

Yes. That is possible, the form that ultimatly calls the create action on the server can have a data-turbo-frame to tell Turbo to replace another frame rather than “this” frame the form is in.

 <%= form_for ...., data: { turbo_frame: "frame_you_want_to_replace" }

This seems like a resonable approach. One thing you might want to change is that. Have an element that acts as the parent of all tires.

<turbo-frame id="new_tier">
      <%= link_to "Add Tier", new_create_subscription_tier_path(sub_type: "Free"), class: "btn btn-primary mb-3 fs-6"
      %>
 </turbo-frame>

<div id="all_tires">

</div>

Then once the tire is created, you can simply “append” to that element.

# app/views/tiers/create.turbo_stream.erb
<%= turbo_stream.append "all_tires" do %>
  <%= render partial: "show" %>
<% end %>

This approach seems better than the “frame” approach you mentioned above. Because frames are best for when “editing” a record. But, for record creation it is “better” to use streams in my opinion.

Hmm, understanding that data-turbo-frame part of the documentation seems like it was the missing piece for me, and it’s still a little unclear.

From the docs:

Sometimes you want most links to operate within the frame context, but not others. This is also true of forms. You can add the data-turbo-frame attribute on non-frame elements to control this:

I’m unsure what the bolded sentence is implying. This seems like it’s talking specifically about link navigation, and doesn’t make clear specifically what the effect is of using data-turbo-frame on a form.

Your explanation makes much more sense. Thank you for your help.

There was another question with same context. And OP confirmed that it actually works on forms. See the answer here Render Turbo Frame Upon Form Submit - #2 by rockwell

Glad it worked out!. Happy to help

1 Like