Stay Ahead in Ruby!
From development processes to the most useful gems, get it all straight in your inbox. Join our Ruby blog today!

Skip to main content

Bootstrap Popovers With Hotwire

Table of Contents
Attention! This article might be outdated, refer to latest documentation if solution does not work.
Bootstrap Popovers With Hotwire - cover image

Wouldn’t it be nice to load Bootstrap popover only when user opens it?
Recently we implemented this feature on one of our Rails projects using Hotwire. It allowed us to show a neat window with the information to make a desk booking in the office.

An example of Bootstrap popover with Hotwire

Book a desk popover

First of all we need to create a Stimulus controller.

/* app/javascript/controllers/desk_controller.js */
/* global bootstrap */

import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  static targets = ["desk"]
  static values = {
    id: Number
  }

  connect () {
    const pop = new bootstrap.Popover(this.deskTarget, {
      html: true,
      sanitize: false,
      trigger: 'manual',
      content: `<turbo-frame id="desk-${this.idValue}" src='/desks/${this.idValue}/book' loading="lazy"></turbo-frame>`
    })
  }

  showPopover {
    bootstrap.Popover.getInstance(this.deskTarget).show()
  }
}

and connect it to the html element

<div data-controller="desk" data-desk-id-value="<%= desk.id %>">
  <div
    class="desk"
    data-desk-target="desk"
    data-action="click->desk#showPopup">
  </div>
<div>

The idea behind the code is to manually create a popover instance with <turbo-frame> inside the content.

  • id - attribute is required later to update the turbo frame with response from Rails.
  • src - path where to load popover content from. Pay attention we can compose it dynamically.
  • loading="lazy" - allows us to trigger a request to /desks/:id/book only when popover is opened.

And last, but not the least, we need to manually open popover via data-action="click->desk#showPopup".

On the Rails side we can now populate <turbo-frame> with content

class DesksController < ApplicationController

  def show
    @desk = Desk.find(params[:id])
    render turbo_stream: turbo_stream.append(
      "desk-#{@desk.id}",
      partial: "show",
      locals: { desk: @desk }
    )
  end
end

Summary #

Here is what actually happens:

graph LR; A[Stimulus connects
controller
to the element]-->B[Popover instance
is created]; B-->C[User clicks div] C-->D[Turbo loads partial
from Rails]

This way we receive SPA-like behaviour with all its perks.

Also using turbo_stream.replace you can later replace <turbo-frame ...> with desk booking result, error messages and etc. inside the popover.

You might need to fix popover positioning after it loads content bootstrap.Popover.getInstance(this.deskTarget).update()

We are ready to provide expert's help with your product
or build a new one from scratch for you!

Contact MobiDev’s tech experts!
MobiDev Ruby Team
Author
Serhii Koba
Group Lead at MobiDev, who likes learning new stuff non-stop. Main languages are: Ruby, Js, Elixir. Doesn’t hesitate playing with DevOps, mobile and IoT.

comments powered by Disqus