Dopo aver introdotto v-show
, v-if
e le direttive associate, continuiamo il nostro viaggio nell’esplorazione dei template in Vue.js e parliamo della direttiva v-for
grazie alla quale riusciamo a mostrare sullo schermo più elementi simili partendo dalle informazioni contenute in un oggetto o in un array.
Ripetere un elemento con la direttiva v-for
In realtà nella forma più semplice, la direttiva v-for
permette di ripetere la stessa struttura di elementi N volte, dove N è un numero intero da noi passato. La sintassi impiegata in questo caso da v-for
è la seguente.
<ul>
<li v-for="num in 5">{{ num }}</li>
</ul>
La direttiva v-for
richiede come espressione la particolare sintassi ‘valoreCorrente in valoreMassimo‘ in cui valoreMassimo può essere un intero o una proprietà definita nell’oggetto data
dell’istanza.
<ul>
<li v-for="n in valoreMassimo">{{ n }}</li>
</ul>
const vm = new Vue({
el: '#root',
data: {
valoreMassimo: 3
}
});
Partendo dal valore 1 e arrivando fino a valoreMassimo
, Vue provvede a ripetere l’elemento (ed eventuali elementi discendenti) su cui è applicata la direttiva v-for
.
In questo caso otteniamo un risultato simile a quello riportato sotto.
Usare la direttiva v-for con degli array
Possiamo usare la direttiva v-for
per iterare sui diversi elementi di un array. La sintassi in questo caso è simile all’esempio visto in precedenza. A v-for
passeremo un valore del tipo ‘elemento in array‘ in cui array
è una proprietà dell’oggetto data
dell’istanza Vue ed elemento
è il nome della variabile che contiene, ad ogni iterazione, un diverso elemento dell’array.
<main id="root">
<ul>
<li v-for="item in shoppingListItems">
{{ item }}
</li>
</ul>
</main>
const vm = new Vue({
el: '#root',
data: {
shoppingListItems: [
'pasta',
'pane',
'mele'
]
}
});
Ovviamente gli elementi dell’array possono anche essere degli oggetti.
const vm = new Vue({
el: '#root',
data: {
shoppingListItems: [
{name: 'farina', price: '1.00'},
{name: 'olio', price: '5.50' },
{name: 'sale', price: '0.50' }
]
}
});
<main id="root">
<ul>
<li v-for="item in shoppingListItems">
{{ item.name }} - {{ item.price }}
</li>
</ul>
</main>
La direttiva v-for
, applicata agli array, consente di avere nell’espressione un secondo argomento opzionale riferito all’indice dell’elemento corrente.
const vm = new Vue({
el: '#root',
data: {
shoppingListItems: [
{name: 'farina', price: '1.00'},
{name: 'olio', price: '5.50' },
{name: 'sale', price: '0.50' }
]
}
});
<main id="root">
<ul>
<li v-for="(item, index) in shoppingListItems">
{{ index }}. {{ item.name }} - {{ item.price }}
</li>
</ul>
</main>
Rilevamento delle modifiche apportate agli array
Vue è stato realizzato in modo da rispondere automaticamente ad eventuali modifiche di un array usato come sorgente nella direttiva v-for
, aggiornando l’applicazione in maniera opportuna.
Entrando più nello specifico, la direttiva v-for
entra in funzione per apportare dei cambiamenti quando vengono invocati i seguenti metodi che modificano l’array su cui sono applicati.
push()
pop()
shift()
splice()
sort()
reverse()
Ma anche quando rimpiazziamo completamente un array con uno nuovo, Vue provvede ad aggiornare opportunamente la lista degli elementi. Vediamo quindi un esempio in cui usiamo il metodo filter()
che restituisce un nuovo array nel quale vengono rimossi gli elementi che non rispettano la condizione formulata tramite funzione.
const vm = new Vue({
el: '#root',
data: {
shoppingListItems: [
{ id: 1, name: 'farina', price: '1.00' },
{ id: 2, name: 'olio', price: '5.50' },
{ id: 3, name: 'sale', price: '0.50' }
]
},
methods: {
filter() {
this.shoppingListItems = this.shoppingListItems.filter(
value => parseFloat(value.price) < 2.5
);
}
}
});
<main id="root">
<button @click="filter">Meno di €2.50</button>
<ul>
<li v-for="item in shoppingListItems" :key="item.id">
{{ item.name }} - €{{ item.price }}
</li>
</ul>
</main>
Bisogna però prestare perticolare attenzione alle situazioni in cui Vue non è in grado di rilevare alcune modifiche. Per esempio, se si cerca di settare il valore di un elemento di un array accedendo tramite l’indice dell’elemento stesso, Vue non avvierà il processo di aggiornamento dell’applicazione e continueremo a vedere nel browser sempre le stesse informazioni.
// Vue non è in grado di rilevare la seguente modifica
vm.shoppingListItems[0] = { id: 1, name: 'pasta', price: '1.00' }
Per effettuare un’operazione simile a quella riportata sopra, possiamo però usare il metodo Vue.set()
come mostrato sotto.
// Vue non è in grado di rilevare la seguente modifica
Vue.set(vm.shoppingListItems, 0, { id: 1, name: 'pasta', price: '1.00' })
Se l’array contiene degli oggetti come elementi, possiamo però andare a modificare direttamente il valore delle proprietà.
vm.shoppingListItems[0].name = 'pasta'
Usare l’attributo key per evitare effetti indesiderati
Vue raccomanda di utilizzare un attributo key
univoco quando si applica la direttiva v-for
. Come vedremo nelle prossime lezioni, key
è obbligatorio quando v-for
viene applicata a dei componenti da noi definiti al fine di mantenere lo stato interno del componente e dei suoi discendenti.
Anche per gli elementi HTML predefiniti è fortemente consigliato usare key
specie quando v-for
è applicata ad elementi che mantengono uno stato interno come avviene per gli elementi di tipo <input>
dei form.
Le ragioni sono simili a quelle già esposte nella lezione precedente quando abbiamo illustrato il funzionamento della direttiva v-if
.
Infatti, al fine di aggiornare gli elementi di una lista costruita con v-for
, Vue usa una strategia definta "in-place patch". Ciò vuol dire che se l’ordine degli elementi di un array cambia, invece di spostare gli elementi del DOM secondo il nuovo ordine, Vue cerca di riutilizzare gli elementi già presenti nel DOM apportando solo le modifiche specifiche alle sezioni che sono state aggiornate.
Questo comportamento permette di ottenere miglioramenti in termini di prestazioni, ma crea comportamenti indesiderati nel caso di elementi che mantengono uno stato interno come i valori dei campi di input di un form. (Abbiamo già fatto un esempio analogo nella precedente lezione per la direttiva v-if
)
Per permettere a Vue di identificare e tenere traccia di ciascun nodo del DOM ed evitare malfunzionamenti inattesi, basterà specificare un attributo key
sugli elementi.
const vm = new Vue({
el: '#root',
data: {
shoppingListItems: [
{id: 1, name: 'farina', price: '1.00'},
{id: 2, name: 'olio', price: '5.50' },
{id: 3, name: 'sale', price: '0.50' }
]
}
});
<ul>
<!-- RICORDA che :key è la forma sintetica di v-bind:key -->
<li
v-for="item in shoppingListItems"
:key="item.id"
>
{{ item.name }} - {{ item.price }}
</li>
</ul>
Mostrare gli elementi di un oggetto con v-for
Con la direttiva v-for
possiamo anche iterare su un oggetto con più proprietà. La forma più semplice è la seguente ed è sostanzialmente uguale a quanto abbiamo già visto per gli array.
const vm = new Vue({
el: '#root',
data: {
list: {
item1: 'pinza',
item2: 'cacciavite',
item3: 'ventosa'
}
}
});
<main id="root">
<ul>
<li v-for="value in list">{{ value }}</li>
</ul>
</main>
Nell’esempio riportato sopra, value
si riferisce al valore di una proprietà. Verrà quindi costruita una lista come quella mostrata nell’immagine sottostante.
Oltre al valore di una proprietà, possiamo accedere anche al suo indice e alla sua chiave passando due parametri addizionali nell’espressione della direttiva v-for
.
<li v-for="(value, key, index) in list">
{{ index }}. {{ key }}: {{ value }}
</li>
Rilevamento delle modifiche apportate agli oggetti
Vue non è in grado di rilevare l’aggiunta o la rimozione di nuove proprietà di un oggetto.
Possiamo tuttavia aggiungere delle proprietà reattive ad un oggetto e quindi permettere a Vue di aggiornare immediatamente le informazioni mostrate sullo schermo in due modi.
Volendo aggiungere una sola proprietà, possiamo usare il metodo Vue.set()
come mostrato sotto.
const vm = new Vue({
el: '#root',
data: {
item: {
name: 'cacciavite',
amount: '3.99',
}
},
methods: {
addQuantity() {
Vue.set(this.item, 'quantity', 10);
}
}
});
<main id="root">
<button @click="addQuantity">Aggiungi Proprietà</button>
<ul>
<li v-for="(value, key) in item">
{{ key }} - {{ value }}
</li>
</ul>
</main>
Se invece preferiamo aggiungere o sovrascrivere più proprietà contemporaneamente, possiamo sfruttare il metodo Object.assign()
per restituire un nuovo oggetto.
const vm = new Vue({
el: '#root',
data: {
item: {
name: 'cacciavite',
}
},
methods: {
addQuantity() {
this.item = Object.assign({}, this.item, {
quantity: 10,
amount: '2.70'
});
}
}
});
Lo stesso risultato dell’esempio appena illustrato si ottiene sfruttando l’operatore Spread (…) introdotto in ES2015.
addQuantity() {
this.item = {...this.item, ...{ quantity: 10, amount: '2.70' }};
}
La direttiva v-for e l’elemento <template>
Le considerazioni fatte per la direttiva v-if
in merito all’uso su più elementi, valgono anche per v-for
. Ricordiamo infatti che andremo ad applicare la direttiva su un solo elemento. Nel caso sia necessario ripetere più elementi, dovremo racchiuderli in un elemento base.
<article v-for="article in articles" :key="article.id">
<h2>{{ article.title }}</h2>
<p>
{{ article.body }}
</p>
</article>
Se non si vuole aggiungere un elemento superfluo, possiamo ricorrere all’elemento <template>
. Applicando v-for
direttamente su <template>
, verranno ripetuti solo gli elementi in esso racchiusi.
<template v-for="item in items">
<h2>{{ item.name }}</h2>
<span>{{ item.price }}</span>
<p>
{{ item.description }}
</p>
<hr />
</template>
Usare contemporaneamente v-for e v-if
Spesso si ha la necessità di usare la direttiva v-for
insieme a v-if
. In questi casi Vue.js raccomanda di NON usare le due direttive sullo stesso nodo. Infatti quando sono presenti sullo stesso elemento, v-for
ha la precedenza su v-if
e accade che l’espressione passata a v-if
venga valutata nuovamente ad ogni iterazione del ciclo.
Nel caso si volesse saltare l’esecuzione del ciclo della direttiva v-for
in base ad una certa condizione, è possibile racchiudere l’elemento su cui è stata applicata v-for
con un elemento base avente v-if
come attributo. Come visto nel precedente paragrafo, possiamo anche usare un elemento wrapper in caso di necessità.
<ul v-if="shoppingListItems.length">
<li v-for="item in shoppingListItems">
{{ item }}
</li>
</ul>
<div v-else>
<p>
Lista della spesa vuota.
</p>
<p>
Aggiungi dei prodotti alla lista.
</p>
</div>
Se invece si ha necessità di mostrare solo degli elementi in base ad una certa condizione, allora si potrà ricorrere a soluzioni alternative (computed properties) che vedremo nelle prossime lezioni.
Riepilogo
In questa lezione abbiamo focalizzato la nostra attenzione sulla direttiva v-for
che, utilizzando una particolare sintassi, consente di ripetere uno o più elementi HTML mostrando le informazioni contenute in un array o in un oggetto. Nella sua forma più semplice, la direttiva v-for
può invece ricevere un numero intero N. In questo caso, l’elemento su cui è stata applicata la direttiva sarà ripetuto nel DOM N volte.