flow-components icon indicating copy to clipboard operation
flow-components copied to clipboard

Support triggering a file download when clicking a button

Open Legioth opened this issue 8 months ago • 1 comments
trafficstars

Describe your motivation

It might not be optimal for accessibility and usability, but lots of users want to trigger downloads from button clicks. The gotcha is that browsers are suspicious of downloads triggered from an XHR response (i.e. a server-side click listener) rather than a direct user interaction. We should instead provide a way of triggering a download from a pre-defined URL immediately when the button is clicked, similarly to how Anchor works.

There's a separate initiative on the Flow side to make it easier to generate the data to download only when a request is sent to a URL rather than having to define things already when the button is configured.

Describe the solution you'd like

Add API to Button for giving it a StreamResource or String URL to download from, e.g. button.setDownloadOnClick(myResource);.

Describe alternatives you've considered

One alternative would be to provide a way of styling an Anchor like a button but that would miss out on existing button functionality such as disable on click.

Additional context

To integrate with the way StreamResource generates URLs and handles requests, the resource needs to be set through Element::setAttribute. There could then be a client-side click listener that starts a download by clicking a temporarily attached <a download href="xyz" /> element.

I did this simple proof of concept without modifying the Button class.

button.getElement().setAttribute("resource", resource);
button.getElement().executeJs("""
        this.addEventListener('click', () => {
          const a = document.createElement('a');
          a.download = '';
          a.href = this.getAttribute('resource');
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
        });
        """);

A proper implementation would additionally have to remove or disable the click listener if the resource is removed and make sure the script is run again if the button has been detached and is attached again.

Legioth avatar Mar 14 '25 09:03 Legioth

There are other use cases where it would be appropriate to style an Anchor to look like a Button (and I don't see any reason why anchors could not have e.g. disabledOnClick). (Not sure about the a11y aspects of download triggers.)

rolfsmeds avatar Mar 19 '25 14:03 rolfsmeds

One additional requirement from https://github.com/vaadin/flow/issues/21929 is to open the resource inline in a new browser tab rather than starting a download.

Legioth avatar Aug 12 '25 07:08 Legioth