How to provide a data file download link? #1328
-
Apologies if this is a basic question. In a Framework project, I'd like to include a link that users can click to download a CSV file of the data for a visualisation, where the data is created by a data loader. Previously I had just linked to the appropriate rendered CSV in |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 6 replies
-
If you create a page in Framework with a link to the resource (say |
Beta Was this translation helpful? Give feedback.
-
Hi! I ended up creating a function and a button to do this. I created a separate js file imported from 'components' that the following function: //download-data-methods.js in "./components/download-data-methods.js"
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
export function convertArraysToJSONStrings(obj) {
const result = { ...obj }
Object.keys(result).forEach((key) => {
if (Array.isArray(result[key])) {
result[key] = JSON.stringify(result[key])
}
})
return result
}
export function download(value, name = "untitled", label = "Save") {
const a = document.createElement("a");
const b = a.appendChild(document.createElement("button"));
b.textContent = label;
a.download = name;
async function reset() {
await new Promise(requestAnimationFrame);
URL.revokeObjectURL(a.href);
a.removeAttribute("href");
b.textContent = label;
b.disabled = false;
}
a.onclick = async (event) => {
b.disabled = true;
if (a.href) return reset(); // Already saved.
b.textContent = "Saving…";
try {
const object = await (typeof value === "function" ? value() : value);
const blob = new Blob([object], { type: "application/octet-stream" });
b.textContent = "Download";
a.href = URL.createObjectURL(blob); // eslint-disable-line require-atomic-updates
if (event.eventPhase) return reset(); // Already downloaded.
a.click(); // Trigger the download
} catch (error) {
console.error("Download error:", error);
b.textContent = label;
}
b.disabled = false;
};
return a;
} In an observable markdown, I then import it, pick a file attachment, and then setup the button. ```js
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
import {convertArraysToJSONStrings, download} from "./components/download-data-methods.js"
```
```js
const cases = FileAttachment("./data/case-issues.csv").csv({typed: true});
```
view(download(async () => {
const csvString = d3.csvFormat(cases);
return new Blob([csvString], { type: "text/csv" });
}, "cases.csv", "Save Table Data")); This creates a button labeled "Save Table Data" that downloads a csv that's properly parsed when 'clicked'. edit: If you have a list of lists that isn't rendering correctly, you can 'unpack' it using the convertArraysTo JSONStrings method e.g., const correctly_unpacked = cases.map(caseObj => convertArraysToJSONStrings(caseObj));
view(download(async () => {
const csvString = d3.csvFormat(cases);
return new Blob([csvString], { type: "text/csv" });
}, "cases.csv", "Save Table Data")); Best of luck! ** @Fil - Updated to d3.csvFormat! Thank you! |
Beta Was this translation helpful? Give feedback.
Currently only links with the
download
attribute are resolved; seeframework/src/html.ts
Line 8 in 2a9c4b5