[LRUG] Turbo - form render on error, redirect on success
    Andrew Stewart 
    boss at airbladesoftware.com
       
    Thu Jan 27 07:18:45 PST 2022
    
    
  
Hello LRUG!
I'm upgrading a Rails 6.1 app to Rails 7 and am stuck on a straightforward problem.  Hopefully I've just overlooked something obvious which a fresh pair of eyes will spot immediately :)
So I have a Thing model.  On the things index page there is a list of things and a new-thing form in a hidden modal at the bottom of the page.  When the user clicks an "Add thing" button the modal opens.
The behaviour I want is:
- when the user submits an invalid form, the form re-renders in the modal with error messages etc;
- when the user submits a valid form, the index page is reloaded – thus clearing away the modal with the filled-in form, showing the newly created thing in the list, and having a fresh new-thing form in a hidden modal.
Unfortunately I'm able to achieve one or the other, but not both.
This pattern of render on error, redirect on success was an issue for a while when Stimulus first came out, and then Turbo, but I thought it was all fixed up now.  Unfortunately searching online mainly yields a gazillion workarounds and hacks from the early days which don't apply any more.
Here's the (pseudo-)code:
# things index page
    <!-- The data attributes are for a Stimulus controller which handles opening/closing modals -->
    = link_to '#', data: {controller: 'modal', action: 'modal#open', modal_id_param: 'thing-form'} do
      Add a new thing
    [ list of things ]
    <div id="thing-form" class="modal" ...modal attributes...>
      ...modal markup...
      = render partial: 'new', locals: {thing: Thing.new}
    </div>
# new thing partial
    = turbo_frame_tag 'thing' do
      = form_with model: thing, id: dom_id(thing) do |f|
        ...input fields...
# new thing page
    = render partial: 'new', locals: {thing: @thing}
# things controller
    def create
      thing = Thing.new thing_params
      if thing.save
        redirect_to things_path, status: :see_other
      else
        render :new, status: :unprocessable_entity  # or we could render the _new partial and ditch the new page
      end
    end
With the code above, an invalid form submission renders into the modal as required, but a successful form submission doesn't reload the index page.  The controller correctly sends a 303 redirect, and Turbo sees it and issues a fresh fetch for the index page – but for some reason doesn't replace/merge the page body.  I'm actually not sure what it does.
I have also tried:
- adding `target: '_top'` to the turbo frame;
- adding `data-turbo-frame="_top"` to the form;
- both of the above.
I can get the results I want if I use turbo stream responses from the controller to "manually" alter the view.  But that feels wrong – like hard-coding something which shouldn't be hard-coded – and would require custom turbo stream responses everywhere else I use a form in a modal.  Also this is a completely standard pattern and I believe that Turbo Drive / Frames should handle it without knowing details of the view.
Any suggestions would be much appreciated!
Many thanks,
Andy Stewart
    
    
More information about the Chat
mailing list