xtlc
xtlc

Reputation: 1368

HX-Trigger to issue a request using arguments

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>

Upvotes: 0

Views: 105

Answers (1)

McPherson
McPherson

Reputation: 1077

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.

Upvotes: 1

Related Questions