Bootstrap Popovers With Hotwire
·2 mins
Table of Contents
Attention! This article might be outdated, refer to latest documentation if solution does not work.
![Bootstrap Popovers With Hotwire - cover image](cover.png)
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](images/bootstrap_hotwire_popover.png)
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]
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!