Open CapnKernel opened 2 years ago
This idea was discussed on the django-htmx channel of the htmx discord server, and one user (@gone) wrote this:
So the way I do messages is with a middleware: https://github.com/gone/animelister/blob/master/animelister/util/middleware.py
and then I generate the slot for it in the base template
https://github.com/gone/animelister/blob/master/animelister/templates/base.html#L31
and then the messages themselves are a partial
https://github.com/gone/animelister/blob/master/animelister/templates/messages.html
because the messages are tagged with oob, they're inserted directly into the messages div regardless of what other kind of content is coming through
I just jam a bunch of them on the end of the html fragment
It seems that oob can be used to piggy-back certain content onto other responses (but as I said, htmx newb here)
https://htmx.org/attributes/hx-swap-oob/ https://htmx.org/examples/update-other-content/
@CapnKernel have you managed to make it work with gone's solution?
gone's solution with hx-swap-oob seems like a generally useful. But I'm not sure we'd want the library to insert messages into every response though - it won't work if you only use htmx on some parts of your site (I doubt most users . It really depends on how you use htmx.
It works well, yeah. The only case it doesn't handle is a boosted link will clear out messages on the page already vs keeping them and merging potential new ones.
@CapnKernel have you managed to make it work with gone's solution?
I haven't tried yet. My coding hasn't started, and this will be my first htmx-using project. Messages was one of two questions I had about practical use of htmx (the other being auto-complete fields such as select2)
I'm not sure we'd want the library to insert messages into every response though
What I suggested was not to include the messages, but just send an event if messages were available. The client may or may not have a handler for that event, and that's ok. That way, messages stay stored until they're consumed (either by a full page load or an htmx request).
That approach sounds like one line of code, not worth adding to the package. Have you tried it?
I didn't submit this myself because I didn't feel like this was a one-size-fits-all case.
Would you be open to a PR expanding the documentation to have a "common problem/setup tasks" section that could show this + and setting up a CSRF token?
Yes it would be nice to expand the documentation. I'd like to set it up with Sphinx so there can be clear sections and nicer styling (using Furo).
any solution for this?
I did that in my code. First I added a HtmxResponseMixin and included that line into the dispatch method:
if messages.get_messages(request):
response.headers["HX-Trigger"] = "messagesAvailable" # or another name...
But this should be generic available, not only in some HTMX views. So I decided to put that into a middleware. But the only purpose for that middleware is adding that trigger - which is a bit overkill.
I second the idea to add this line in django-htmx' middleware, as it is a general purpose solution (if the messages framework is used in your project).
The problem with the oob solution is definitively: you can swap messages into a tag by hx-swap-oob
, but this only works as you don't use e.g. bootstrap toasts, they are hidden by default, and just shown by using Js in the frontend. Having a trigger in the header you can rely on when using HTMX calls makes everything a bit easier, and more django-ish
// e.g. in base.html / script tag
const messagesToastElement = document.getElementById("messages-toast")
const messagesToast = new bootstrap.Toast(messagesToastElement, { delay: 2000 })
htmx.on("messagesAvailable", () => {
messagesToast.show()
})
You don't have to use the Hx-Trigger
, but if it's there, it's a big help.
I'd rather not set up the middleware to always inject the header. It will add response overhead for apps that don't use it. As you point out, it's two lines of code - I wouldn't say it's "overkill" to write your own middleware for that, middleware classes are cheap :)
I am leaning towards only writing a section in the "tips" page on messages. I think we could document the two techniques: a middlweare to trigger an event (using django_htmx.http.trigger_client_event()
) with on-page handler, and OOB.
Btw toast messages are pretty tricky accessibility-wise, for example see this post that summarizes how to meet a11y criteria when using them. They suggest a minimum delay of 6 seconds, based on message length. Personally I've decided to stick to messages the user has to deliberately close, or navigate away from, rather than using a timer.
@adamchainz definitely. Usability-wise there are differences we (in medical software) suffer all the time, because most of that software is Bad™. Lots of alert fatique. Therefore you need >=3 kinds of messages:
Many of the messages in medical (EMR) software are 3, but should be 2 or even 1.
If you use messages without toasts, just in a <div>
, they use space, and if you click them away, the content below jumps up, which is very unconvenient IMHO. But if you make the div floating, we arrived at toasts again...
What has that to do with Django/HTMX? There should be a possibility to implement 1.-3. with HTMX.
And what I didn't know, is that Middlewares are cheap. Easy to built, yes, but I (until now) tried to avoid having too much different middlewares, as they are called at each request and seemed to add an unnecessary overhead to the processing. But if you assure that that's not the case, I'm perfectly fine with adding that line into my own middleware (as I did ATM).
+1 for adding some docs for both methods. Both have dis/advantages. OOB urges you to add the OOB div with queued messages to EACH request (so you need another middleware to inject that? Or a mixin/helper for your view? Or a htmx.html
base template you extend each and every template that is sent via HTMX over the wire?
all these approaches sound weird to me, tried them all a bit, won't do them again.
I would suggest to add messages using a header/Hx-Trigger with a custom event and show them using htmx.on()
in the frontend. You can decide then if you want toasts or other divs anyway.
This also should cover async responses (be it channels or the upcoming Django async features?) when pushed events from the server include that messages.
@bblanchon told me he'd work on such a middleware ATM - I'm keen to see that soon.
@CapnKernel Follow this great tutorial with Toasts and Django https://blog.benoitblanchon.fr/django-htmx-toasts/
That works perfectly. IMHO there is no need to implement that in django-htmx, as it's one level upwards.
Description
I'd like to start using htmx (means I'm an htmx newbie). Is there a recommended way of handling messages created in views with code such as
messages.success(request, 'All good')
?Perhaps
HtmxMiddleware
could checkrequest._messages._queued_messages
, and if that's set, add aHX-Trigger
response header to the response, and this could cause the client to make a request back to the server to collect the messages.https://github.com/django/django/blob/main/django/contrib/messages/middleware.py#L22 https://github.com/django/django/blob/main/django/contrib/messages/storage/base.py#L149
Might also be possible to do it this way:
https://docs.djangoproject.com/en/dev/ref/contrib/messages/#expiration-of-messages
I don't know what I'm talking about. I just would like Django messages to appear, and if django-htmx can assist this in a nice way...