Skip to content

πŸ“„ 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>.

ts
type ExportFormat<T> = {
  id: string
  label: string
  extension: string
  mimeType: string
  serialize: (data: T) => string
}

Minimal custom format example ​

ts
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 ​

vue
<DataExporter
  :data="payload"
  :formats="['json', csvFormat]"
  default-format="csv"
/>

When used through BaseDataTable, prefer customising via the exporter slot:

vue
<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 headers are kept
  • hidden/omitted columns are excluded from export
  • the table actions column 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:

Scopeserialize receives
Itemsa bare items array
Metaa 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):

  1. Normalise β€” map the three incoming shapes onto { data: { items, meta? }, itemsOnly, metaOnly }:

    • array β†’ { items: raw }, itemsOnly: true
    • object with items array β†’ full envelope, both flags false
    • object without items β†’ treat as meta only: { items: [], meta: raw }, metaOnly: true
  2. Serialize XRechnung β€” after normalisation:

    • itemsOnly β€” use a minimal invoice: default currency for document-level codes, emit only <cac:InvoiceLine> blocks from data.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.items is 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:

  • header
  • format-selector
  • scope-switch
  • preview-toggle
  • preview-content
  • actions

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 TypeError messages for invalid input.
  • Keep errors actionable ("expected { items, meta? } or items[]").
  • Fail fast in serialize instead of returning partial/broken output silently.