February 27, 2021

Forms inside modals with Hotwire


In my previous article I’ve explained how to create modals using Hotwire. In this article I will show you how to best handle forms inside modals.

Creating a page in Spina CMS

As an example I will show you how Spina’s new page form works.
<!— views/pages/new.html.erb —>
<turbo-frame id=“modal”>
  <%= form_with model: @page, data: {turbo_frame: “_top”} do |f| %>
    <%= f.text_field :title %>
    
    <%= f.submit %>
  <% end %>
</turbo-frame>
Setting data-turbo-frame to _top makes sure that after submission, the user is correctly redirected to the created page. If you don’t add this attribute, the response will be rendered inside of the modal frame.

Rendering errors

As long as your form does not have errors, this setup already works. Sadly, forms do have errors sometimes. Because our form is rendered inside a modal, we don’t want to re-render the entire page. Instead, we extract the page form in a partial and wrap that in another turbo-frame. 
<!— views/pages/new.html.erb —>
<turbo-frame id=“modal”>
  <turbo-frame id=“new_page”>
  
    <%= render “new_page” %>
  
  </turbo-frame>
</turbo-frame>

<!— views/pages/_new_page.html.erb —>
<turbo-frame id=“new_page”>
  <%= form_with model: @page, data: {turbo_frame: “_top”} do |f| %>
    <%= f.object.errors.full_messages.join(“<br />”).html_safe %>
    <%= f.text_field :title %>
    
    <%= f.submit %>
  <% end %>
</turbo-frame>
Now for the magic that ties it all together: add a Turbo stream response when the form contains errors. 
# controllers/pages_controller.rb
def create
  @page = Page.new(page_params)
  if @page.save
    redirect_to @page
  else
    render turbo_stream: turbo_stream.update(“new_page”, partial: “new_page”)
  end
end
Now when the form is saved successfully, the user will be redirected to @page. And when the form cannot be saved, it will re-render the form partial and update it inside your modal using Turbo Streams. Including errors. Neat!