Using Modals with Turbo-Frames

The Goal

To create a modal form and make it appear in your current view. The following is extracted from live production code.

How do we do it?

We’ll need:

  • Stimulus JS
  • Turbo Rails
  • (And in my case: Bootstrap + JQuery)
## Some view 
 <%= turbo_frame_tag "new_group" do %>
      <%= link_to "New group", new_group_path %>
    <% end %>

Explanation of the above code snippet: When we are on a particular page, we want to create a new group without navigating away from that page. Why? In order to make it a better user experience.

The user hits the new group link and is presented with the following modal. I’m using bootstrap. I have elided most of the complex bits so you can copy/paste according to your own needs:

# groups/new.html.erb
<div class="container">
  <%= turbo_frame_tag "new_group" do %> <!-- Note the turbo frame -->

  <!-- note the stimulus controller -->
    <div data-controller="modal" class="modal" tabindex="-1" role="dialog">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title"> Create Group</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">  

<------- Im reusing this partial hence the locals passed in ------->          
            <%= render "groups/form", locals: {url: group_path(@project), method: :post} %>
          </div>
          <div class="modal-footer">            
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
          </div>
        </div>
      </div>
    </div>

<------- We add this, so after the modal is cancelled etc. we can still have a button to open up the modal again ------->          
    <%= link_to "New group", new_group_path() %>
  <% end %>
</div>

We need a controller to activate the modal the second it appears in the dom. And we want to hide it the second it disappears.

/// modal_controller.js
import { Controller } from "stimulus";

export default class extends Controller {
  connect(){       
    $(this.element).modal();        
  }

  disconnect() {    
    $(this.element).modal('hide');      
  }  
}

# groups/show.html.erb
 <%= turbo_frame_tag "new_group" do %>
  <!-- simply display what you want to display when a new group is created -->
 <% end %>
# finally redirect to the show action upon successful creation of the group record

## groups_controller

def create
  if @group.save
    redirect_to @group
  end
end

I’m by no means an expert on the intricacies of turbo-rails, so I might have missed something - if so please let me know.

I may post a giphy to show how it works, if I get the opportunity.

I hope this helps!

Written on January 22, 2021