Composables
useCrudView
Gestione UI CRUD (dialog + editing + delete con conferma) sopra una lista reattiva. Include useCrudViewFromResource per il bridge automatico.
Signature
function useCrudView<T extends Identifiable, CreateRequest, UpdateRequest = Partial<CreateRequest>>(
options: CrudViewOptions<T, CreateRequest, UpdateRequest>,
): CrudViewResult<T, CreateRequest, UpdateRequest>
function useCrudViewFromResource<T extends Identifiable, CreateRequest, UpdateRequest = Partial<CreateRequest>>(
resource: CrudResourceResult<T, CreateRequest, UpdateRequest>,
options: CrudViewFromResourceOptions<T>,
): CrudViewResult<T, CreateRequest, UpdateRequest>
Astrae il pattern dialogVisible + editingItemId + handleCreate/handleEdit/handleSave/handleDelete ricorrente in viste lista CRUD.
Headless by design: toast e confirm non sono importati dalla libreria — sono callback iniettate dal MFE (es.
useToast()/useConfirm() di PrimeVue). Così la libreria resta agnostica rispetto alla UI library.Type parameters
| Parameter | Vincolo | Descrizione |
|---|---|---|
T | extends Identifiable ({ id: string }) | Entità della lista |
CreateRequest | — | Payload create |
UpdateRequest | default Partial<CreateRequest> | Payload update |
Opzioni CrudViewOptions
interface CrudViewOptions<T extends Identifiable, CreateRequest, UpdateRequest = Partial<CreateRequest>> {
items: Ref<T[]>
loadItems?: () => Promise<void>
createItem: (payload: CreateRequest) => Promise<T>
updateItem?: (id: string, payload: UpdateRequest) => Promise<T>
deleteItem?: (id: string) => Promise<void>
resourceName: string
singularName?: string
getDisplayName?: (item: T) => string
showErrorToast?: (message: string) => void
showDeleteConfirm?: (message: string, header: string, onAccept: () => void) => void
}
| Opzione | Tipo | Default | Descrizione |
|---|---|---|---|
items | Ref<T[]> | — | Lista reattiva (tipicamente da useCrudResource) |
loadItems | () => Promise<void> | — | Reload dopo save/delete (opzionale) |
createItem | (payload) => Promise<T> | — | Obbligatorio |
updateItem | (id, payload) => Promise<T> | — | Se omesso, handleSave userà sempre createItem |
deleteItem | (id) => Promise<void> | — | Se omesso, handleDelete lancia errore |
resourceName | string | — | Nome plurale (es. 'clienti') |
singularName | string | resourceName | Nome singolare per messaggi ("Conferma Eliminazione cliente") |
getDisplayName | (item) => string | item => item.id | Estrae nome visualizzato (es. (c) => c.ragioneSociale) |
showErrorToast | (message) => void | — | Callback toast in caso di errore save/delete |
showDeleteConfirm | (message, header, onAccept) => void | — | Callback confirm dialog. Senza, elimina direttamente senza conferma |
Risultato CrudViewResult
interface CrudViewResult<T, CreateRequest, UpdateRequest> {
dialogVisible: Ref<boolean>
editingItemId: Ref<string | null>
formKey: Ref<number>
editingItem: ComputedRef<T | undefined>
handleCreate: () => void
handleEdit: (id: string) => void
handleSave: (payload: CreateRequest | UpdateRequest) => Promise<void>
handleDelete: (id: string) => void
}
| Campo | Descrizione |
|---|---|
dialogVisible | v-model per <Dialog> |
editingItemId | null = creazione, string = modifica |
formKey | Incrementato a ogni handleCreate/handleEdit → forza re-mount del form (reset stato interno) |
editingItem | Item corrente da items.value.find(i => i.id === editingItemId.value) |
handleCreate() | Apre dialog in modalità creazione (editingItemId = null) |
handleEdit(id) | Apre dialog in modalità modifica |
handleSave(payload) | Se editingItemId !== null && updateItem: update; altrimenti create. Chiude dialog, ricarica, gestisce errori con toast |
handleDelete(id) | Mostra confirm con messaggio "Eliminare definitivamente {singularName} '{displayName}'? L'operazione non è reversibile." e poi elimina |
Esempio manuale
import { ref } from 'vue'
import { useCrudView } from '@pzeta/mfe-contracts/composables'
import { useToast, useConfirm } from 'primevue'
interface Cliente { id: string; ragioneSociale: string; piva: string }
interface CreateClienteRequest { ragioneSociale: string; piva: string }
const items = ref<Cliente[]>([])
const toast = useToast()
const confirm = useConfirm()
const view = useCrudView<Cliente, CreateClienteRequest>({
items,
resourceName: 'clienti',
singularName: 'cliente',
getDisplayName: (c) => c.ragioneSociale,
loadItems: async () => { items.value = await fetchClienti() },
createItem: async (p) => await apiPostCliente(p),
updateItem: async (id, p) => await apiPutCliente(id, p),
deleteItem: async (id) => { await apiDeleteCliente(id) },
showErrorToast: (msg) =>
toast.add({ severity: 'error', detail: msg, life: 5000 }),
showDeleteConfirm: (message, header, onAccept) =>
confirm.require({ message, header, accept: onAccept }),
})
<template>
<button @click="view.handleCreate">Nuovo</button>
<table>
<tr v-for="c in items" :key="c.id">
<td>{{ c.ragioneSociale }}</td>
<td>
<button @click="view.handleEdit(c.id)">Modifica</button>
<button @click="view.handleDelete(c.id)">Elimina</button>
</td>
</tr>
</table>
<Dialog v-model:visible="view.dialogVisible.value">
<ClienteForm
:key="view.formKey.value"
:cliente="view.editingItem.value"
@save="view.handleSave"
/>
</Dialog>
</template>
Bridge: useCrudViewFromResource
Quando hai già un useCrudResource, useCrudViewFromResource collega automaticamente i due composables ed elimina il wiring manuale:
import {
useCrudResource,
useCrudViewFromResource,
} from '@pzeta/mfe-contracts/composables'
const resource = useCrudResource<Cliente, CreateClienteRequest>({
resourceName: 'clienti',
loadItems: /* ... */,
createItem: /* ... */,
updateItem: /* ... */,
deleteItem: /* ... */,
})
const view = useCrudViewFromResource(resource, {
singularName: 'cliente',
getDisplayName: (c) => c.ragioneSociale,
showErrorToast: /* ... */,
showDeleteConfirm: /* ... */,
})
Internamente collega resource.items → view.items, resource.load → view.loadItems, e così via.
Opzioni CrudViewFromResourceOptions
interface CrudViewFromResourceOptions<T extends Identifiable> {
singularName?: string
getDisplayName?: (item: T) => string
showErrorToast?: (message: string) => void
showDeleteConfirm?: (message: string, header: string, onAccept: () => void) => void
}
Solo aspetti di presentazione: tutto il resto è ereditato dal resource.
Comportamento handleDelete
1. Trova l'item: items.find(i => i.id === id)
2. Costruisce displayName via getDisplayName(item)
3. Costruisce message: "Eliminare definitivamente {singularName} '{displayName}'? L'operazione non è reversibile."
4. Costruisce header: "Conferma Eliminazione {singularName}"
5. Se showDeleteConfirm fornita:
→ mostra confirm con onAccept = doDelete()
altrimenti:
→ esegue doDelete() direttamente
Tipi correlati
useCrudResource— sorgente datiuseDialog— alternativa più semplice per dialog ad-hocuseErrorToast