π Custom Export Formats β
Create a custom export format when built-ins (json, yaml, xml) are not enough.
Format contract β
A custom format implements ExportFormat<T>.
type ExportFormat<T> = {
id: string
label: string
extension: string
mimeType: string
serialize: (data: T) => string
}Minimal custom format example β
import type { ExportFormat } from "@raclettejs/core/orchestrator/composables"
type CsvItem = { id: string; name: string }
export const csvFormat: ExportFormat<CsvItem[]> = {
id: "csv",
label: "CSV",
extension: "csv",
mimeType: "text/csv",
serialize(items) {
const header = "id,name"
const rows = items.map((i) => `${i.id},${i.name}`)
return [header, ...rows].join("\n")
},
}Register custom format β
<DataExporter
:data="payload"
:formats="['json', csvFormat]"
default-format="csv"
/>When used through BaseDataTable, prefer customising via the exporter slot:
<BaseDataTable
:items="rows"
:headers="headers"
:show-export="true"
>
<template #exporter="{ data, disabled, formats, defaultFormat, isExportMode }">
<DataExporter
v-if="isExportMode"
:data="data"
:disabled="disabled"
:formats="formats"
:default-format="defaultFormat"
button-label="Export selected"
/>
</template>
</BaseDataTable>When used via BaseDataTable β
If DataExporter is used through BaseDataTable, selected rows are projected before serialization:
- only keys from the table
headersare kept - hidden/omitted columns are excluded from export
- the table
actionscolumn is excluded
So custom serializers should not assume every original row property is always present.
Handle exporter scopes correctly β
DataExporter passes one of three shapes into serialize, depending on the scope switch:
| Scope | serialize receives |
|---|---|
| Items | a bare items array |
| Meta | a bare meta object (no items key) |
| Combined | { items, meta? } |
Invoice formats (XRechnung, ZUGFeRD) usually normalise that into a single BillingData plus flags, then branch. The OpenStack billing plugin does this in billingHelpers.ts as normaliseBillingExport and implements the split in xRechnungFormat.ts (logic onlyβno need to paste the XML templates here):
Normalise β map the three incoming shapes onto
{ data: { items, meta? }, itemsOnly, metaOnly }:- array β
{ items: raw },itemsOnly: true - object with
itemsarray β full envelope, both flags false - object without
itemsβ treat as meta only:{ items: [], meta: raw },metaOnly: true
- array β
Serialize XRechnung β after normalisation:
itemsOnlyβ use a minimal invoice: default currency for document-level codes, emit only<cac:InvoiceLine>blocks fromdata.items, omit supplier, customer, invoice id, and tax totals (downstream fills or another export supplies them).- Otherwise β full invoice:
resolveMeta(data.meta)for parties, VAT, invoice number, due date;calcTotals(data.items, vatRate)for monetary summaries; same line loop as above. For meta-only scope,data.itemsis empty: you still get the header block, with line list and totals reflecting no consumption lines.
That is why meta is useful in real workflows: the same serializer can export header-only party and commercial terms (JSON/YAML for another system, or XML with no lines) without dragging along selected VM rowsβwhile items does the opposite, and combined ships one complete document.
Customising exporter UI via slots β
DataExporter now provides dedicated UI slots in addition to the default trigger slot:
headerformat-selectorscope-switchpreview-togglepreview-contentactions
Use these when you need to adapt structure/styling while reusing the export logic (copyToClipboard, downloadFile, format handling, scope selection).
Error handling recommendations β
- Throw clear
TypeErrormessages for invalid input. - Keep errors actionable ("expected
{ items, meta? }oritems[]"). - Fail fast in
serializeinstead of returning partial/broken output silently.