Data dialogs and exports
This page belongs to Data App developer guide. The menu that offers these actions is in Object menu.
Show an object’s data
Section titled “Show an object’s data”The visualization packages do not expose the query or the rows behind a mounted object, and they have no export API at the moment:
- Offer data actions — show as table, CSV, Excel — only for data your app queries itself, such as a figure card or a hand-built chart, and read those rows from the engine, not from the DOM.
- Do not present a query you rebuilt from a visualization’s fields as that visualization’s data. Top N, sorting, grouping and the object’s own filters live inside the visualization, so the rows can differ from what it shows.
Open the dialog immediately
Section titled “Open the dialog immediately”Open the dialog immediately with a loading indicator and disable exports until the data arrives.
Create a new request token every time the dialog opens and invalidate it on close. Apply a result only if its token is still current, including when the same object is reopened.
Display query errors in the dialog.
For CSV files intended to be opened in Excel:
- Start with a UTF-8 byte-order mark. Without it, Excel reads UTF-8 as the system’s legacy code page
and
Förbrukningarrives asFörbrukning. - Put
sep=;on the first line and separate with semicolons. Excel reads a comma-separated file as one column in locales that use the comma as a decimal separator. - Quote every field that contains a semicolon, a quote or a newline, and double the quotes inside it. An unquoted semicolon shifts every later column.
- Neutralize text that a spreadsheet would run as a formula. Model values are data, and a text
value starting with
=,+,-,@, a tab or a carriage return can be evaluated when the file is opened. Prefix such text with an apostrophe; leave real numbers as numbers.
const csvField = (value: unknown) => { if (typeof value === 'number') return String(value); let text = value == null ? '' : String(value); if (/^[=+\-@\t\r]/.test(text)) text = `'${text}`; return /[;"\r\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text;};const csv = '\uFEFF' + ['sep=;', ...rows.map((row) => row.map(csvField).join(';'))].join('\r\n');Offer a real .xlsx as well as CSV, so numbers arrive as numbers and the file opens without an
import dialog. Use a library you trust, or write the file directly — an .xlsx is a zip of a few
XML parts. If you write it yourself:
- Write numbers as numeric cells (
<c r="B2"><v>1234.5</v></c>) so Excel can sum them, and text as inline strings. - Remove control characters from text. XML 1.0 cannot carry them, and Excel refuses a file that contains one.
- Storing the zip entries uncompressed avoids needing a deflate implementation; you still need a correct CRC-32 for each entry.
- A fixed timestamp in the zip entries makes the output byte-identical between runs.
Check the archive’s CRCs and load it with a spreadsheet library to verify cell values and types.
Downloads
Section titled “Downloads”Create the file as a Blob, point a temporary link at an object URL and click it. Revoke the URL on
the next animation frame, not in the same tick; the click is handled asynchronously and an
immediately revoked URL can be gone before the browser reads it.
Export an object as an image
Section titled “Export an object as an image”To capture a region, clone its subtree,
wrap it in an SVG foreignObject, draw that through an <img> onto a canvas, and export the canvas
at twice its size for sharpness. Preserve the following when cloning:
- Styles. An SVG image fetches no stylesheets, so inline every node’s computed style. Large markup is normal.
- Canvas pixels. A
<canvas>serializes as an empty element, and charts are drawn to canvases. Replace each with an<img>holding its owntoDataURL(). - Pseudo-elements.
::beforeand::afterare not cloned. Re-create any that carry content — an arrow on a delta, for example — as real elements. - Scroll position. A clone starts at the top. For each scrolled container, shift its contents by a transform instead.
- Icon fonts. An SVG image loads no external fonts, and a cross-origin stylesheet cannot be read back to inline. Replace icon glyphs with inline SVG or a font you embed, mapping by code point rather than ligature name.
- Virtualized content. A table renders only the rows and columns in view. To capture more of it, resize the live object, wait for it to re-render, capture, then restore it; widening the clone gives headings with no cells.
- Serialize with
XMLSerializer, notinnerHTML, and set the XHTML namespace on the clone. An SVG image parses its content as XML, and HTML serialization loads as nothing. - Paint a background. A transparent image of a dark panel disappears on a white slide; fill with the page’s own background colour.
Canvas export works only while the canvas is not tainted. A cross-origin image in the region makes
toDataURL() throw, and a WebGL canvas without a preserved drawing buffer reads back blank. Report
those cases rather than saving a blank or partial image.