Components

DynamicFormDrawerWithTable

Drawer laterale con DataTable integrata e form CRUD completo per gestione record tramite dialog.

Panoramica

DynamicFormDrawerWithTable combina in un unico componente:

  • Un Drawer laterale (default: lato destro, dimensione large)
  • Una DataTable con paginazione e selezione singola
  • Una toolbar con pulsanti Nuovo/Modifica/Elimina
  • Un dialog form (via DynamicFormDialog) per creazione/modifica record

Gestisce autonomamente il ciclo CRUD completo: carica i dati, consente la selezione di un record, apre il form precompilato per la modifica, e salva le modifiche chiamando le funzioni fornite.

Caratteristiche principali:

  • Caricamento dati all'apertura del drawer e al cambiamento dei filtri
  • Toolbar configurabile (pulsanti abilitabili/disabilitabili singolarmente)
  • Pulsanti Modifica/Elimina abilitati solo con una riga selezionata
  • Gestione errori con evento error
  • Columne tabella configurabili con template slot

Casi d'uso tipici: gestione anagrafica, configurazione categorie, assegnazione ruoli, qualsiasi entità CRUD con lista + form.

Anteprima live (con CRUD mockato)

Utilizzo

<!-- Codice reale con API -->
<script setup lang="ts">
const showDrawer = ref(false)

// Definizione colonne tabella
const colonne = [
  { field: 'id',    header: 'ID',    sortable: true, width: '80px' },
  { field: 'nome',  header: 'Nome',  sortable: true },
  { field: 'email', header: 'Email', sortable: true },
  { field: 'stato', header: 'Stato', template: 'colStato' },  // Slot personalizzato
]

// Schema del form (stessa struttura di DynamicForm)
const schemaForm = [{
  name: 'nome',
  type: 'text',
  label: 'Nome',
  props: { required: true }
}, {
  name: 'email',
  type: 'text',
  label: 'Email',
  props: { required: true }
}]

// Funzioni CRUD
const fetchUtenti = async (filters?: FilterCriteria) => {
  return api.getUtenti(filters)
}
const createUtente = async (data: Record<string, unknown>) => {
  return api.createUtente(data)
}
const updateUtente = async (id: string | number, data: Record<string, unknown>) => {
  return api.updateUtente(id, data)
}
const deleteUtente = async (id: string | number) => {
  return api.deleteUtente(id)
}
</script>

<template>
  <Button label="Gestisci utenti" @click="showDrawer = true" />

  <DynamicFormDrawerWithTable
    v-model:visible="showDrawer"
    :schema="schemaForm"
    :columns="colonne"
    id-field="id"
    :fetch-all-fn="fetchUtenti"
    :create-fn="createUtente"
    :update-fn="updateUtente"
    :delete-fn="deleteUtente"
    header="Gestione Utenti"
    @after-save="handleSave"
    @after-delete="handleDelete"
    @error="handleError"
  >
    <!-- Slot per colonna con template personalizzato -->
    <template #colStato="slotProps">
      <Tag
        :value="slotProps.data.stato"
        :severity="slotProps.data.stato === 'attivo' ? 'success' : 'danger'"
      />
    </template>
  </DynamicFormDrawerWithTable>
</template>

Con filtri statici

<!-- Codice reale con API -->
<DynamicFormDrawerWithTable
  v-model:visible="show"
  :schema="schema"
  :columns="colonne"
  id-field="id"
  :fetch-all-fn="fetchUtenti"
  :filters="{ ruolo: 'operatore', attivo: true }"
  :create-fn="create"
  :update-fn="update"
  :delete-fn="del"
  header="Operatori attivi"
/>

Solo lettura (senza modifica/elimina)

<!-- Codice reale con API -->
<DynamicFormDrawerWithTable
  v-model:visible="show"
  :schema="schema"
  :columns="colonne"
  id-field="id"
  :fetch-all-fn="fetch"
  :toolbar-buttons="{ insert: true, modify: false, delete: false }"
  header="Visualizza record"
/>

Props

PropTipoDefaultDescrizione
visiblebooleanVisibilità del drawer (v-model:visible)
schemaFormSchemaSchema del form (vedi DynamicFormLayouts → FormSchema)
columnsColumnDefinition[]Configurazione colonne della DataTable
idFieldstringNome del campo identificativo (chiave primaria)
fetchAllFn(filters?: FilterCriteria) => Promise<Record<string, unknown>[]>Funzione per caricare tutti i record
createFn(data: Record<string, unknown>) => Promise<unknown>Funzione per creare un nuovo record
updateFn(id: string | number, data: Record<string, unknown>) => Promise<unknown>Funzione per aggiornare un record
deleteFn(id: string | number) => Promise<unknown>Funzione per eliminare un record
filtersFilterCriteriaFiltri statici passati a fetchAllFn
headerstring''Titolo del drawer
position'left' | 'right' | 'top' | 'bottom''right'Posizione del drawer
size'small' | 'medium' | 'large' | 'full''large'Dimensione del drawer (mappa alla prop size del Drawer di @pzeta/vue-components)
toolbarButtons{ insert?: boolean, modify?: boolean, delete?: boolean }Tutti trueConfigura quali pulsanti mostrare
drawerClassstringDeprecata e non funzionante: il Drawer di @pzeta/vue-components usa <Teleport> e non eredita l'attributo class. Usare size. Verrà rimossa in una major successiva

Struttura ColumnDefinition

interface ColumnDefinition {
  field: string          // Nome proprietà nell'oggetto dati
  header?: string        // Intestazione colonna (default: field)
  sortable?: boolean     // Abilita ordinamento
  hidden?: boolean       // Nasconde la colonna
  width?: string         // Larghezza CSS (es. '120px', '10%')
  class?: string         // Classe CSS aggiuntiva
  template?: string      // Nome slot per template personalizzato
}

Slot

I template per le colonne con template specificato vengono passati come slot con il nome corrispondente:

<!-- Column: { field: 'stato', template: 'colStato' } -->
<template #colStato="slotProps">
  <!-- slotProps.data = riga dati corrente -->
  <!-- slotProps.field = nome campo -->
  <Tag :value="slotProps.data.stato" />
</template>

Comportamento CRUD

AzioneTriggerComportamento
CaricaApertura drawer, cambio filtriChiama fetchAllFn con filters correnti
NuovoClick "Nuovo"Apre dialog form vuoto; al save chiama createFn
ModificaClick "Modifica" (riga selezionata)Apre dialog precompilato; al save chiama updateFn(id, data)
EliminaClick "Elimina" (riga selezionata)Chiama deleteFn(id) senza conferma; ricarica la griglia

Nota: L'eliminazione non mostra dialogo di conferma. Aggiungere conferma lato applicazione se necessario, intercettando l'evento error o avvolgendo deleteFn con una logica di conferma personalizzata.

Dialog form

Il dialog usa DynamicFormDialog (componente interno) e riceve:

  • Lo schema fornito al componente
  • initialData = riga selezionata (in modalità modifica) o oggetto vuoto (in modalità creazione)
  • La callback save che esegue createFn o updateFn e ricarica la griglia

Eventi

EventoPayloadDescrizione
update:visiblebooleanCambio visibilità drawer
rowSelectedRecord<string, unknown>Riga selezionata nella tabella
afterSaveRecord<string, unknown>Dopo salvataggio riuscito (create o update)
afterDeleteRecord<string, unknown>Dopo eliminazione riuscita
errorErrorErrore in qualsiasi operazione CRUD