Composables

useExport

Composable principale per generare ed eseguire un export.

Panoramica

useExport gestisce la selezione campi, formato, filtri, ordinamento, applicazione di template salvati e l'esecuzione dell'export. Distingue tra export sincrono (Blob restituito direttamente) e asincrono (server risponde con 202 Accepted e exportId, segue polling). In entrambi i casi il file viene scaricato automaticamente con downloadBlob.

Internamente usa useExportTemplates per la gestione dei modelli e usePolling per il tracking degli export asincroni.

Inizializzazione

import { useExport } from '@pzeta/vue-importexport'

const {
  loading, errore,
  risorsa, campiDisponibili, campiSelezionati,
  formato, nomeFile, etichettaRisorsa,
  filtri, ordinamento, filtriPredefiniti, ordinamentoPredefinito,
  exportResult, exportProgresso, isPolling,
  templateManager,
  loadMetadata, applyTemplate,
  toggleField, selectAllFields, deselectAllFields, moveField,
  executeExport, cancelExport, reset,
} = useExport()

Parametri

ParametroTipoDefaultDescrizione
apiOverrideIExportClient & ITemplateClientuseApi()Client da usare; senza, quello fornito con provide(IMPORT_EXPORT_API_KEY)

Stato reattivo

ProprietàTipoDescrizione
loadingRef<boolean>Operazione in corso
erroreRef<string | null>Messaggio errore
risorsaRef<string>Codice risorsa
campiDisponibiliRef<CampoMetadato[]>Campi esportabili (caricati da loadMetadata)
campiSelezionatiRef<string[]>Nomi campo nell'ordine scelto dall'utente
formatoRef<FileFormato>xlsx (default) o csv: gli unici che node-excel-export produce
nomeFileRef<string>Nome file forzato; vuoto (default) = nome calcolato, vedi Nome del file
etichettaRisorsaRef<string>Nome della risorsa per l'utente: se c'è, dà il nome al file (dalla 0.6.0)
filtriRef<Record<string, unknown>>Filtri del modello applicato
ordinamentoRef<OrdinamentoExport[]>Ordinamento del modello applicato
filtriPredefinitiRef<Record<string, unknown>>Filtri di chi apre l'export (es. la pagina filtrata): valgono se il modello non ha filtri (dalla 0.6.0)
ordinamentoPredefinitoRef<OrdinamentoExport[]>Ordinamento di chi apre l'export: vale se il modello non ha ordinamento (dalla 0.6.0)
exportResultRef<ExportResult | null>Risultato finale (sync o async)
exportProgressoRef<ExportProgresso | null>Progresso polling per async
isPollingRef<boolean>Polling attivo
templateManageroggettoRestituito da useExportTemplates

Metodi

loadMetadata(codiceRisorsa)

Imposta risorsa.value, carica campiDisponibili da getExportFieldMetadata e i template della risorsa via templateManager.loadTemplates.

await loadMetadata('anagrafica')
// campiDisponibili.value popolato
// templateManager.templates.value popolato

applyTemplate()

Applica il template attualmente selezionato (templateManager.selectedTemplate): popola campiSelezionati, formato, filtri, ordinamento.

  • Un modello senza filtri o senza ordinamento li azzera: non eredita quelli del modello applicato prima, e tornano a valere i predefiniti.
  • Un modello salvato fino alla 0.5.5 con formato json, che il servizio non produce, lascia il formato corrente.

toggleField(campo), selectAllFields(), deselectAllFields(), moveField(from, to)

Manipolano campiSelezionati. moveField riordina spostando un elemento da from a to (drag-and-drop dal PickList).

executeExport()

Esegue la chiamata POST /export/execute. Comportamento:

  1. Costruisce ExportRequest da stato corrente: campi, filtri e ordinamento effettivi (quelli del modello, o i predefiniti se il modello non li ha), modelloId, nome del file senza estensione
  2. Se la risposta è un Blob → download immediato + exportResult settato a { tipo: 'sincrono', stato: 'completato' }
  3. Se la risposta è asincrona (202):
    • stato: 'in_corso' → parte polling
    • urlDownload presente → download immediato
    • stato: 'errore' → errore propagato

cancelExport()

Annulla l'export tramite AbortController. Ferma anche l'eventuale polling in corso.

reset()

Azzera lo stato dell'export: campi, formato, nomeFile, filtri e ordinamento del modello, risultato, polling, errori, template selezionato. etichettaRisorsa, filtriPredefiniti e ordinamentoPredefinito restano: li imposta chi apre l'export.

Nome del file

Se nomeFile è vuoto il nome si calcola alla data di oggi:

CondizioneNomeDalla
etichettaRisorsa valorizzata<etichetta> <AAAA-MM-GG>.<formato> («Storico consegne 2026-10-15.xlsx»)0.6.0
altrimenti<risorsa>_<AAAA-MM-GG>.<formato> («anagrafica_2026-10-15.xlsx»)0.2.8

Al servizio va come nomeFile lo stesso nome senza estensione. Il nome indicato dal servizio per un export asincrono (nomeFile del risultato) ha la precedenza.

Tipo ExportRequest

interface ExportRequest {
  risorsa: string
  formato: FileFormato
  campi: string[]
  /** Passati intatti a node-excel-export: vedi «Filtri e ordinamento» in ExportDialog */
  filtri?: Record<string, unknown>
  /** Criteri in ordine di priorità, al massimo 10 */
  ordinamento?: OrdinamentoExport[]
  /** Nome del file senza estensione: il servizio aggiunge quella del formato */
  nomeFile?: string
  modelloId?: string
}

interface OrdinamentoExport {
  campo: string
  direzione: 'asc' | 'desc'
  /** Posizione dei NULL (dalla 0.7.0, node-excel-export ≥ 1.1.15); senza, il default di PostgreSQL */
  nulls?: 'first' | 'last'
}

Formati di filtri e ordinamento, e versioni del servizio che li supportano: ExportDialog › Filtri e ordinamento della pagina.

Tipo ExportResult

Discriminated union su tipo:

type ExportResult =
  | {
      tipo: 'sincrono'
      stato: 'completato'
      nomeFile: string
    }
  | {
      tipo: 'asincrono'
      exportId: string
      stato: 'completato' | 'in_corso' | 'errore'
      nomeFile?: string
      dimensione?: number
      righeEsportate?: number
      urlDownload?: string
      tempoEsecuzione?: number
    }

Per distinguere i due casi basta il discriminante, che TypeScript restringe da solo:

if (result.tipo === 'asincrono') {
  console.log('Export asincrono:', result.exportId)
}
isAsyncExportResult, definita accanto ai tipi, non è esportata a runtime: src/index.ts riesporta i tipi con export type *, che porta i tipi ma non le funzioni. Importarla dal pacchetto dà errore di compilazione.

Esempio

const exporter = useExport()

await exporter.loadMetadata('anagrafica')
exporter.selectAllFields()
exporter.formato.value = 'xlsx'
exporter.etichettaRisorsa.value = 'Anagrafiche'   // file: «Anagrafiche 2026-10-15.xlsx»
exporter.filtriPredefiniti.value = { attivo: true }
exporter.ordinamentoPredefinito.value = [
  { campo: 'cognome', direzione: 'asc' },
  { campo: 'datanascita', direzione: 'desc', nulls: 'last' },
]

await exporter.executeExport()
// Sincrono: file scaricato, exportResult.value.stato = 'completato'
// Async: polling parte, exportProgresso.value si aggiorna