flow-components
flow-components copied to clipboard
Support triggering a file download when clicking a button
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.
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.)
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.