I thought to share a pattern I've been using recently, for providing modal popups, via HTMX. I'm using Bootstrap5 but this is straightforward to implement in whichever framework. As ever, HTMX has a wonderful example of how to do this in the frontend (as well any many other examples!). Here though I show how I'm doing things both front and backend, and specifically for Django. I'm doing things a little differently than the official example, cutting out some lines of code. You can find the repo (including sqlite db -- admin username and password is 'me') here: https://github.com/andytwoods/htmxDjangoModalPopup
Above is our project layout. I like to hold all my htmx files in a partials folder within my templates for a given app.
from django.db import models
class EightiesKidsTVShows(models.Model):
name = models.CharField(max_length=64)
decade = models.IntegerField(default=1980)
blurb = models.TextField()
url = models.URLField()
image_url = models.URLField()
Setting the scene, we have a minimal model, containing information about kids shows from the 80s (the best era for kids shows -- my own kids would roll their eyes).
<div class="modal-body"></div>
...
{% for show in shows %}
<tr>
<td>
<a class="btn btn-primary" hx-post="{{ request.path }}?id={{ show.id }}" hx-target=".modal-body">
{{ show.name }}</a>
</td>
<td>{{ show.decade }}</td>
</tr>
{% endfor %}
Within our template, we iterate over our shows, and use htmx to post an id back to our view. Purists may argue that I should not be attaching the id by means of a url parameter, and instead use the htmx 'include-vals' extension. I feel though that my approach achieves it's goal in maybe 20 characters, whist 'include-vals' is a lot more wordy (requiring additional html nodes to store the parameters). Intercooler, the predecessor to htmx, has the 'ic-include' tool right out of the box (you added your variables in json format ic-include="{'a':'scoobydoo', 'b': 'scrappy'}"), which I find myself wishing perhaps was not removed in htmx.
Note that when someone clicks on a show.name, htmx POSTS the server with the show.id, and the server returns rendered content. That content replaces the contents of the target 'modal-body'.
from django.shortcuts import render
from popup.models import EightiesKidsTVShows
def popup(request):
if request.htmx:
id = request.GET.get('id')
context = {'show': EightiesKidsTVShows.objects.get(id=id)}
return render(request, 'popup/partials/htmx_show_popup.html', context=context)
context = {'shows': EightiesKidsTVShows.objects.all()}
return render(request, 'popup/popup.html', context=context)
Our view detects if htmx is POSTing (using Adam Johnson's django-htmx). If so, we retrieve the desired information from the database and render content to display in the modal on the front end.
<div class="modal-body">
<h1>{{ show.name }}</h1>
<img src="{{ show.image_url }}" alt="show image">
<div>{{ show.blurb }}</div>
<script>
var myModalEl = document.getElementById('myModal');
var modal = bootstrap.Modal.getInstance(myModalEl);
modal.toggle();
</script>
</div>
Our partial, our small template we render upon POST request, contains a script that runs when the content replaces 'modal-body'. Note that we wrap our content in a div with the 'modal-body' class so we can replace this content in the future.