Passa al contenuto

Rendering delle Liste

v-for

Per mostrare una lista di elementi basati su un array possiamo utilizzare la direttiva v-for. La direttiva v-for richiede una sintassi speciale nella forma di item in items, dove items è l'array di dati sorgente e item è un alias per l'elemento dell'array su cui si sta iterando:

js
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
js
data() {
  return {
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
template
<li v-for="item in items">
  {{ item.message }}
</li>

All'interno dello scope di v-for, le espressioni del template hanno accesso a tutte le proprietà dello scope del genitore. Inoltre, v-for supporta anche un secondo alias opzionale per l'indice dell'elemento corrente:

js
const parentMessage = ref('Parent')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
js
data() {
  return {
    parentMessage: 'Parent',
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
template
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
  • Parent - 0 - Foo
  • Parent - 1 - Bar
  • Il contesto delle variabili in v-for è simile al seguente JavaScript:

    js
    const parentMessage = 'Parent'
    const items = [
      /* ... */
    ]
    
    items.forEach((item, index) => {
      // ha accesso allo scope/contesto esterno per `parentMessage`
      // ma `item` e `index` sono disponibili solo qui
      console.log(parentMessage, item.message, index)
    })

    Nota come il valore v-for corrisponda alla dichiarazione della funzione nella callback di forEach. Puoi utilizzare, infatti, il destructuring sull'alias dell'elemento v-for in modo simile al destructuring degli argomenti della funzione:

    template
    <li v-for="{ message } in items">
      {{ message }}
    </li>
    
    <!-- con un alias per index -->
    <li v-for="({ message }, index) in items">
      {{ message }} {{ index }}
    </li>

    Per v-for annidati, anche lo scope funziona in modo simile alle funzioni annidate. Ogni scope di v-for ha accesso agli scope dei genitori:

    template
    <li v-for="item in items">
      <span v-for="childItem in item.children">
        {{ item.message }} {{ childItem }}
      </span>
    </li>

    È possibile utilizzare anche of come delimitatore al posto di in, così da avvicinarsi maggiormente alla sintassi di JavaScript per gli iteratori:

    template
    <div v-for="item of items"></div>

    v-for con un Oggetto

    Puoi anche utilizzare v-for per iterare attraverso le proprietà di un oggetto. L'ordine dell'iterazione sarà basato sul risultato dell'invocazione di Object.keys() sull'oggetto:

    js
    const myObject = reactive({
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    })
    js
    data() {
      return {
        myObject: {
          title: 'How to do lists in Vue',
          author: 'Jane Doe',
          publishedAt: '2016-04-10'
        }
      }
    }
    template
    <ul>
      <li v-for="value in myObject">
        {{ value }}
      </li>
    </ul>

    Puoi anche fornire un secondo alias per il nome della proprietà (anche noto come key):

    template
    <li v-for="(value, key) in myObject">
      {{ key }}: {{ value }}
    </li>

    E un altro per l'indice (index):

    template
    <li v-for="(value, key, index) in myObject">
      {{ index }}. {{ key }}: {{ value }}
    </li>

    v-for con un Intervallo

    v-for può accettare anche un numero intero. In questo caso, ripeterà il template quel numero di volte, basandosi su un intervallo da 1 a n.

    template
    <span v-for="n in 10">{{ n }}</span>

    Nota che n con un valore iniziale di 1 al posto di 0.

    v-for su <template>

    Come con v-if, nel template puoi usare un tag <template> anche con v-for per renderizzare un blocco di elementi multipli. Ad esempio:

    template
    <ul>
      <template v-for="item in items">
        <li>{{ item.msg }}</li>
        <li class="divider" role="presentation"></li>
      </template>
    </ul>

    v-for con v-if

    Nota

    Non è consigliato utilizzare v-if e v-for sullo stesso elemento a causa della precedenza implicita. Fare riferimento alla guida di stile per i dettagli.

    Quando esistono sullo stesso nodo, v-if ha una priorità maggiore rispetto a v-for. Ciò significa che la condizione v-if non avrà accesso alle variabili dallo scope di v-for:

    template
    <!--
    Questo genererà un errore perché la proprietà "todo"
    non è definita sull'istanza.
    -->
    <li v-for="todo in todos" v-if="!todo.isComplete">
      {{ todo.name }}
    </li>

    Questo problema può essere risolto spostando v-for in un tag <template> contenitore (che è anche più esplicito):

    template
    <template v-for="todo in todos">
      <li v-if="!todo.isComplete">
        {{ todo.name }}
      </li>
    </template>

    Mantenere lo Stato con key

    Quando Vue aggiorna un elenco di elementi resi con v-for, di default utilizza una strategia di "correzione in loco" (in-place patch). Se l'ordine degli elementi dei dati viene modificato, invece di spostare gli elementi DOM per far corrispondere l'ordine degli elementi, Vue correggerà in-place ogni elemento e si assicurerà che rifletta ciò che dovrebbe essere reso in quel particolare indice.

    Questa modalità predefinita è efficiente, ma è adatta solo quando l'output della visualizzazione del tuo elenco non dipende dallo stato del componente figlio o dallo stato temporaneo del DOM (ad es. valori di input del modulo).

    Per permettere a Vue di identificare ogni nodo cosi da poter riutilizzare e riordinare gli elementi esistenti, è necessario fornire un attributo key unico per ogni elemento:

    template
    <div v-for="item in items" :key="item.id">
      <!-- contenuto -->
    </div>

    Quando si utilizza <template v-for>, la key deve essere posizionata sul contenitore <template>:

    template
    <template v-for="todo in todos" :key="todo.name">
      <li>{{ todo.name }}</li>
    </template>

    Nota

    L'attributo key qui è uno speciale attributo legato a v-bind, e non va confuso con la "chiave" della proprietà di un oggetto, come quando si utilizza v-for con un oggetto.

    Si raccomanda di fornire un attributo key con v-for ogni volta che è possibile, a meno che il contenuto DOM iterato non sia semplice (cioè non contenga componenti o elementi DOM con stato), o si stia volutamente facendo affidamento sul comportamento predefinito per aumentare le prestazioni.

    Il binding della key si aspetta valori primitivi, cioè stringhe e numeri. Non utilizzare oggetti come chiavi per v-for. Per un utilizzo dettagliato dell'attributo key, si prega di consultare la documentazione dell'API key.

    v-for con un Componente

    Questa sezione presuppone la conoscenza dei Componenti. Sentiti libero di saltarla e tornare in seguito.

    Puoi utilizzare direttamente v-for su un componente, come su un qualsiasi elemento, ma non dimenticare di fornire una key:

    template
    <MyComponent v-for="item in items" :key="item.id" />

    Tuttavia, questo non passerà alcun dato al componente in modo automatico, poiché i componenti hanno i propri scope isolati. Per passare i dati iterati al componente, dovremmo utilizzare anche le props:

    template
    <MyComponent
      v-for="(item, index) in items"
      :item="item"
      :index="index"
      :key="item.id"
    />

    La ragione per non iniettare item nel componente automaticamente è che ciò renderebbe il componente strettamente accoppiato al funzionamento di v-for. Essere chiari sull'origine dei dati rende il componente adattabile a diverse situazioni.

    Guarda questo esempio di una semplice lista di todo per vedere come rendere un elenco di componenti utilizzando v-for, passando dati diversi a ogni istanza.

    Guarda questo esempio di una semplice lista di todo per vedere come rendere un elenco di componenti utilizzando v-for, passando dati diversi a ogni istanza.

    Rilevare le modifiche all'Array

    Metodi di mutazione

    Vue è in grado di rilevare quando vengono chiamati i metodi di mutazione di un array reattivo e di attivare gli aggiornamenti necessari. Questi metodi di mutazione sono:

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()

    Sostituire un Array

    I metodi di mutazione (Mutation methods), come suggerisce il nome, modificano l'array originale su cui vengono chiamati. Esistono anche metodi non mutanti, come filter(), concat() e slice(), che non modificano l'array originale ma restituiscono sempre un nuovo array. Quando si lavora con metodi non mutanti, si dovrebbe sostituire il vecchio array con uno nuovo:

    js
    // `items` è un ref con valore di array
    items.value = items.value.filter((item) => item.message.match(/Foo/))
    js
    this.items = this.items.filter((item) => item.message.match(/Foo/))

    Potresti pensare che ciò costringerà Vue a scartare il DOM esistente e a ri-renderizzare l'intero elenco - fortunatamente, non è così. Vue implementa alcune euristiche intelligenti per massimizzare il riutilizzo degli elementi del DOM, quindi sostituire un array con un altro array contenente oggetti che si sovrappongono è un'operazione molto efficiente.

    Visualizzare Risultati Filtrati/Ordinati

    A volte vogliamo visualizzare una versione filtrata o ordinata di un array senza effettivamente modificare o reimpostare i dati originali. In questo caso puoi creare una proprietà calcolata (computed property) che restituisce l'array filtrato o ordinato.

    Ad esempio:

    js
    const numbers = ref([1, 2, 3, 4, 5])
    
    const evenNumbers = computed(() => {
      return numbers.value.filter((n) => n % 2 === 0)
    })
    js
    data() {
      return {
        numbers: [1, 2, 3, 4, 5]
      }
    },
    computed: {
      evenNumbers() {
        return this.numbers.filter(n => n % 2 === 0)
      }
    }
    template
    <li v-for="n in evenNumbers">{{ n }}</li>

    Nelle situazioni in cui le computed properties non sono utilizzabili (ad es. all'interno di cicli v-for annidati), è possibile utilizzare un metodo:

    js
    const sets = ref([
      [1, 2, 3, 4, 5],
      [6, 7, 8, 9, 10]
    ])
    
    function even(numbers) {
      return numbers.filter((number) => number % 2 === 0)
    }
    js
    data() {
      return {
        sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
      }
    },
    methods: {
      even(numbers) {
        return numbers.filter(number => number % 2 === 0)
      }
    }
    template
    <ul v-for="numbers in sets">
      <li v-for="n in even(numbers)">{{ n }}</li>
    </ul>

    Fai attenzione con reverse() e sort() in una computed property! Questi due metodi modificheranno l'array originale, il che dovrebbe essere evitato nei getter delle computed property. Crea una copia dell'array originale prima di chiamare questi metodi:

    diff
    - return numbers.reverse()
    + return [...numbers].reverse()
    Rendering delle Liste has loaded