back to top

Template in Vue.js: Mostrare gli elementi di array o oggetti con v-for

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.

uso della direttiva v-for con intervallo di numeri

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'
    ]
  }
});
esempio direttiva v-for con array

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>
https://vimeo.com/462643261

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.

Esempio con la direttiva v-for applicata su un oggetto

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.

Pubblicitร