Loading Dynamic Content on Opening Bootstrap Modals with Stimulus JS

·

5 min read

When working on a recent project for Zonmaster.com, I encountered a requirement that called for loading dynamic data into a Bootstrap 5 modal every time it was opened. By default, Bootstrap modals are rendered during the page load and remain static thereafter, which can cause performance issues if the modal’s content is resource-intensive. To overcome this limitation and ensure that the modal always displays up-to-date information, I sought a solution that involved fetching data from the server each time the modal was opened.

In this article, I’ll walk you through the steps I took to implement this feature using Stimulus JS, a lightweight JavaScript framework. By leveraging Stimulus’ action-response pattern, we can dynamically load the modal’s content from the server and provide a seamless user experience. So, whether you need to display real-time data, user-specific information, or dynamic content in your Bootstrap modals, this solution will ensure that your users always have the most relevant information at their fingertips.

The usual Bootstrap 5 modal

Usually, you have the modal that looks like this:

<div class="modal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <p>Modal body text goes here.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

And you open it with a button on the page that looks like this

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
  Launch demo modal
</button>

In this case, the modal actually renders on page load. When the page that has the modal is loaded the modal is rendered. Anything that happens after that doesn’t affect the modal. Also, if what the modal does is a little heavy, then your page load suffers.

The solution

I ended up ditching the standard button and doing this instead

<button type="button" class="btn btn-sm btn-success" 
    data-action="click->my-modal#openModal" 
  data-controller="my-modal"
  data-my-modal-mymodal-value="myModal"
  data-my-modal-url-param="/my_path"> <i class="fa-duotone fa-face-thinking"></i> Open Me </button>

In order to handle the opening of the modal and fetching data from the server, we’ll leverage the power of the Stimulus JS framework. Stimulus follows a pattern where actions on the page trigger responses defined in a Stimulus controller. In our case, we’ll use the “click” action on a button to call a method within the controller, allowing us to perform the necessary operations when the button is clicked.

Create the controller:

rails g stimulus myModal

To get started, let’s create a Stimulus controller specifically for handling the modal functionality. This controller will define the method that will be called when the button is clicked, and it will orchestrate the loading of data into the modal. By encapsulating the modal logic within a controller, we keep our code organized and maintain a clear separation of concerns.

Your controller will look like this

import { Controller } from "@hotwired/stimulus";

// Connects to data-controller="my-modal"
export default class extends Controller {
  static values = { mymodal: String };

  initialize() {
    // This method is triggered when the Bootstrap modal is loaded
    // You can perform any initialization or setup here
  }

  openModal({ params: { url } }) {
    // Open the Bootstrap modal
    new bootstrap.Modal(this.modalTarget).show();

        // Fetch the data remotely
    fetch(url)
      .then((response) => response.text())
      .then((html) => {
        // Update the modal content with the fetched data
        this.modalContentTarget.innerHTML = html;
      });
  }

  get modalTarget() {
    return this.findModal();
  }

  get modalContentTarget() {
    return this.modalTarget.querySelector(".modal-content");
  }

  findModal() {
    return document.querySelector(this.myModalValue);
  }
}

When the button associated with the modal is clicked, the Stimulus controller’s method, let’s call it openModal, will be invoked with the URL parameter (we sent that parameter on the button earlier). Inside this method, we can perform actions such as immediately opening the modal and displaying a loading indicator to provide feedback to the user that something is happening.

To retrieve the dynamic content for the modal, we’ll utilize the fetch API, a JavaScript feature that allows us to make asynchronous requests to the server. In our case, we'll send a request to the server to fetch the rendered data specific to the modal. Once the response is received, we can extract the necessary data and update the content of the modal.

Since Bootstrap modals have a consistent structure, we can easily locate the outer container using its ID. By targeting this container, we can update its content with the data retrieved from the server. This approach allows us to refresh the modal’s content every time it is opened, ensuring that users always see the latest information.

My modal ends up being very simple

<div class="modal fade" id="myModal" tabindex="-1" aria-labelledby="myModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-xl">
    <div class="modal-content">
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="myModalLabel"><i class="fa-duotone fa-face-thinking zm-light-color"></i> My Info</h1>
        <span class='ms-3'>Here's what you're looking for</span>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <div class="spinner-border text-primary" role="status">
          <span class="visually-hidden">Loading...</span>
        </div>
      </div>
      <div class="modal-footer">
      </div>
    </div>
  </div>
</div>

I hope this article has provided you with a valuable approach to loading dynamic content into Bootstrap modals using Stimulus JS. By adopting this technique, you can ensure that your modals always display the most up-to-date information, providing a seamless user experience.

If you found this article helpful and are interested in further expanding your knowledge of web development, I invite you to check out my YouTube channel. On my channel, I cover various topics related to web development, including Ruby on Rails. Whether you’re a beginner or an experienced developer, my goal is to provide educational content that can assist you in your learning journey. I also release articles on my Substack and my Medium channel.

Additionally, I’m more than happy to offer assistance and support to developers who may be facing challenges in developing their Ruby on Rails applications. If you’re stuck on a particular problem or could benefit from some guidance, feel free to reach out to me. I’m open to the idea of pair programming sessions, where we can work together to overcome obstacles and improve your Rails development skills.

Remember, the best way to grow as a developer is through collaboration and continuous learning. Let’s support each other and create a vibrant community where we can all thrive.

Thank you for reading, and happy coding!