I want to create a request after a trigger using htmx after a button was clicked with django:
def SomeView(request, var1, var2):
trigger_data = json.dumps({"dlstart": {"pid": pid, "var1": var1}})
return HttpResponse("", headers={"HX-Trigger": trigger_data})
I can see in the Network Tab the GET request is working. But now I want to add the two arguments from trigger_data
to my hx-get
dynamically. I did not find a way in htmx alone to achieve this, so I did a JS workaround (which I would gladly drop in favor of a htmx only solution):
document.body.addEventListener("dlstart", function() {
const url = `/someURL/${event.detail.pid}/${event.detail.var1}`;
const filename = "test.png";
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('was not ok');
}
return response.blob();
})
.then(blob => {
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click(); // feels so ugly
document.body.removeChild(link);
})
});
I imagine some combination of hx-get
and dynamically add the arguments must work too?
Edit:
SomeView
is just being called by a click on a button.
def DLView(request, pid, var1):
## stuff
prod = Product.objects.get(id=pid)
pdf = pdf_create(prod, var1)
with open(pdf, "rb") as pdf_file:
response = HttpResponse(pdf_file.read(), content_type="application/pdf")
response["Content-Disposition"] = f"""attachment; filename="{pdf}" """ # filename not used, see js part
return response
urls:
path("someurl/<int:pid>/<str:var1>", DLView, name="some-url"),
In the solution suggested I added a modal save button like this:
<button type="submit"
id="start-dl-button"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'start-dl' pid var1 %}"
hx-on::before-request="document.getElementById('modal-container-{{ pid }}').innerHTML=''">
Start DL
</button>
It is pretty sneaky to download a server-generated file with htmx-powered requests. But there is an easy workaround that avoids any custom JavaScript at all.
The general idea is to have a view that only offers the download option to the client, like you're doing. The trick then is to simply redirect the request to that view.
In your case, first refactor SomeView
:
from django.urls import reverse_lazy
def SomeView(request, var1, var2):
dl_url = reverse_lazy("some-url", kwargs=dict(pid=pid, var1=var1))
return HttpResponse(headers={"Hx-Redirect" : dl_url})
This will issue a client redirect to the supplied url. Then in the DLView
, return a FileResponse
that makes is easy to offer a download option to the client:
from django.http import FileResponse
def DLView(request, pid, var1):
# do stuff
pdf = pdf_create(prod, var1)
return FileResponse(open(pdf, "rb"), as_attachment=True, filename="test.pdf")
This view should do what your JavaScript is doing by opening a download dialog in the browser. I hope that's what you wish to accomplish.
Note:
It's a convention that function names in Python should be in snake_case
, while class names should be in PascalCase
. Thus, instead of def DLView...
, it should be def dl_view...
.
EDIT
To close the modal before the download starts you can listen to afterRequest event on the button, and add any modal-closing logic there:
<button hx-on::after-request="document.getElementById('modal-id').classList.remove('is-active')" ...>Print </button>
I am not familiar with Bulma so include whatever logic you're using to close the modal.