Components

PickListRemote

Doppia lista per trasferimento elementi con caricamento remoto: spostamento tra lista disponibili e lista selezionati.

Panoramica

PickListRemote è un componente a doppia lista che carica gli elementi disponibili da una sorgente remota e consente di spostarli tra una lista "source" (disponibili) e una lista "target" (selezionati). Il valore emesso è l'array degli elementi nel target.

Caratteristiche principali:

  • Caricamento asincrono elementi disponibili tramite fetchFn
  • In alternativa a fetchFn, la chiamata PostgREST può essere composta dal componente dichiarando la risorsa: Sorgente dati endpoint
  • Separazione automatica source/target basata su modelValue
  • Spostamento singolo o multiplo con pulsanti freccia
  • Controlli di ordinamento per ogni lista
  • Template personalizzato per la visualizzazione degli elementi
  • Gestione eventi moveToTarget e moveToSource

Casi d'uso tipici: assegnazione utenti a ruoli/gruppi, selezione permessi da assegnare, configurazione colonne visibili, selezione prodotti per un bundle.

Anteprima live (con fetchFn mockato)

Utilizzo nello schema

const schema = [{
  name: 'utentiAssegnati',
  type: 'picklist',
  label: 'Assegna utenti al gruppo',
  props: {
    fetchFn: fetchTuttiUtenti,
    optionLabel: 'nomeCompleto',
    optionValue: 'id',
    sourceTitle: 'Utenti disponibili',
    targetTitle: 'Utenti nel gruppo',
  }
}]

Utilizzo diretto

<PickListRemote
  v-model="utentiAssegnati"
  :fetch-fn="fetchTuttiUtenti"
  option-label="nomeCompleto"
  option-value="id"
  source-title="Utenti disponibili"
  target-title="Utenti assegnati"
  label="Assegnazione utenti"
  @move-to-target="handleAssegna"
  @move-to-source="handleRevoca"
/>

Con template personalizzato

<PickListRemote
  v-model="permessiSelezionati"
  :fetch-fn="fetchPermessi"
  option-value="codice"
  source-title="Permessi disponibili"
  target-title="Permessi assegnati"
>
  <template #option="{ option }">
    <div class="flex items-center gap-2">
      <i :class="option.icona" class="text-blue-500" />
      <div>
        <div class="font-medium">{{ option.nome }}</div>
        <div class="text-xs text-gray-500">{{ option.categoria }}</div>
      </div>
    </div>
  </template>
</PickListRemote>

Come funziona la sincronizzazione source/target

// All'arrivo dei dati da fetchFn:
// - allData = tutti gli elementi dal server
// - modelValue = elementi già nel target
// Il componente calcola:
//   source = allData.filter(item => !targetValues.includes(item[optionValue]))
//   target = modelValue

// Esempio:
// allData = [{id:1, nome:'Mario'}, {id:2, nome:'Luigi'}, {id:3, nome:'Peach'}]
// modelValue = [{id:2, nome:'Luigi'}]
// source = [{id:1, nome:'Mario'}, {id:3, nome:'Peach'}]
// target = [{id:2, nome:'Luigi'}]

Sorgente dati endpoint

In alternativa a fetchFn, PickListRemote può comporre da sé la chiamata PostgREST a partire dalla risorsa dichiarata:

props: {
  endpoint: 'auth.permessi',
  order: 'descrizione.asc',
  optionLabel: 'descrizione',
  optionValue: 'idpermesso'
}

fetchFn ha sempre la precedenza: se sono presenti entrambe, endpoint viene ignorato. La forma dell'endpoint, la traduzione dei filtri e l'ordine con cui viene risolto il trasporto sono descritti in Sorgente dati endpoint.

Props

PropTipoDefaultDescrizione
modelValueArray<Record<string, unknown>> | null[]Elementi nel target (v-model)
labelstringEtichetta del campo
helperTextstringTesto di supporto sotto il campo
errorstringMessaggio di errore (attiva stato invalid)
disabledbooleanfalseDisabilita il trasferimento
invalidbooleanfalseAttiva visivamente lo stato di errore
fetchFnFetchFnFunzione per caricare tutti gli elementi disponibili
filterstring | endpoint`string
selectstringColonne da selezionare (solo con endpoint)
orderstringOrdinamento PostgREST, es. 'nome.asc' (solo con endpoint)
limitnumberNumero massimo di righe restituite (solo con endpoint)
baseUrlstring''Prefisso del gateway PostgREST, se l'endpoint dichiara uno schema
keyNamestring'id'Chiave primaria della risorsa (solo client PostgREST standalone)
FilterCriteria`Filtro statico passato alla fetchFn
optionLabelstring'label'Proprietà da usare come etichetta
optionValuestring'value'Proprietà da usare come identificatore univoco
sourceTitlestring(da i18n: 'Disponibili')Titolo lista sorgente
targetTitlestring(da i18n: 'Selezionati')Titolo lista destinazione
dataKeystring(uguale a optionValue)Chiave univoca per PrimeVue PickList
scrollHeightstring'14rem'Altezza area scrollabile di ogni lista
showSourceControlsbooleantrueMostra pulsanti di ordinamento nella lista source
showTargetControlsbooleantrueMostra pulsanti di ordinamento nella lista target

Slot

SlotPropsDescrizione
sourceheaderHeader lista source (sovrascrive sourceTitle)
targetheaderHeader lista target (sovrascrive targetTitle)
option{ option, index }Template personalizzato per ogni elemento

Metodi esposti

MetodoDescrizione
reload()Ricarica tutti gli elementi chiamando nuovamente la fetchFn

Eventi

EventoPayloadDescrizione
update:modelValueArray<Record<string, unknown>>Elementi aggiornati nel target
moveToTargetArray<Record<string, unknown>>Elementi appena spostati nel target
moveToSourceArray<Record<string, unknown>>Elementi appena spostati nel source

Nota: Gli eventi move-all-to-target e move-all-to-source di PrimeVue vengono internamente mappati su moveToTarget/moveToSource.

Validazione suggerita

import { z } from 'zod'

const schema = z.object({
  utentiAssegnati: z.array(z.object({ id: z.number() }))
    .min(1, 'Assegna almeno un utente')
    .max(10, 'Massimo 10 utenti per gruppo'),
})