Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Middleware assistance for Django messages? #152

Open
CapnKernel opened this issue Nov 19, 2021 · 16 comments
Open

Middleware assistance for Django messages? #152

CapnKernel opened this issue Nov 19, 2021 · 16 comments

Comments

@CapnKernel
Copy link

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 check request._messages._queued_messages, and if that's set, add a HX-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...

@CapnKernel
Copy link
Author

CapnKernel commented Nov 19, 2021

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/

@Frohus
Copy link

Frohus commented Jan 7, 2022

@CapnKernel have you managed to make it work with gone's solution?

@adamchainz
Copy link
Owner

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.

@gone
Copy link
Contributor

gone commented Jan 11, 2022

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
Copy link
Author

@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)

@CapnKernel
Copy link
Author

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).

@adamchainz
Copy link
Owner

That approach sounds like one line of code, not worth adding to the package. Have you tried it?

@gone
Copy link
Contributor

gone commented Jan 14, 2022

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?

@adamchainz
Copy link
Owner

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).

@Junaidiqbal35
Copy link

any solution for this?

@nerdoc
Copy link

nerdoc commented Sep 12, 2022

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.

@adamchainz
Copy link
Owner

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.

@adamchainz
Copy link
Owner

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.

@nerdoc
Copy link

nerdoc commented Sep 13, 2022

@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:

  1. Non-disturbing ("successfully saved file") - this does not need a closing X and can be auto-closing after 6s.
  2. Important ones that should be noticed. ("Error: there is no file to save to." e.g.). MUST be closed manually.
  3. Blockers. Users can't get further without clicking on a button.

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.

@ericmuijs
Copy link

@CapnKernel Follow this great tutorial with Toasts and Django https://blog.benoitblanchon.fr/django-htmx-toasts/

@nerdoc
Copy link

nerdoc commented Dec 29, 2022

That works perfectly. IMHO there is no need to implement that in django-htmx, as it's one level upwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants