La directiva v-for
es muy interesante para crear estructuras repetitivas de código HTML de una forma sencilla y sin que el código resulte excesivamente complejo (sobre todo en estructuras que se repiten muchas veces).
La estructura de un v-for
es muy sencilla y se basa en la posibilidad de crear un bucle for desde las templates de código HTML de Vue. Teniendo en cuenta su sintaxis, puedes crear código complejo en muy pocas lineas, basado en el bucle forEach de Javascript:
const array = ["Gato", "Perro", "Pájaro", "Dinosaurio"];
// Sintaxis simple (sólo necesitamos el item)
array.forEach(item => console.log(item));
// Sintaxis compleja (necesitamos el índice y el item)
array.forEach((item, index) => console.log(index, item));
Así pues, si en Vue aplicamos una directiva v-for
en nuestro código HTML, la sintaxis quedaría algo similar a que veremos a continuación. La variable item
es una variable local del bucle que tendrá el valor de cada ítem del array en cada iteración:
<template>
<div>
<div v-for="item in array">{{ item }}</div>
</div>
</template>
<script>
export default {
name: "BaseBlock",
data() {
return {
array: ["HTML", "CSS", "Javascript", "Terminal"],
};
},
};
</script>
En el ejemplo anterior se debe añadir también la directiva :key
, que explicaremos a continuación. Se trata de otra directiva en la que profundizaremos más adelante, pero primero vamos a ver su finalidad junto al v-for
.
La directiva :key
Aunque el código anterior es teóricamente correcto, si intentamos ejecutarlo, probablemente obtendremos un error del linter en el cuál se nos avisa que se esperaba una directiva v-bind:key
junto al v-for
y no se ha encontrado:
Elements in iteration expect to have 'v-bind:key' directives
Aún no hemos visto las directivas v-bind
, pero en este caso, básicamente se nos pide identificar de forma única cada elemento de un array, y nos pide que lo asociemos con una clave que sea única. Por lo tanto, lo correcto sería aplicar a todo elemento que lleve un v-for
, un atributo :key
al que se le asigne un valor único del ítem.
Observa también que nosotros podríamos querer que el array
tuviera valores repetidos (no es el caso), por lo que quizás el propio nombre del ítem no sea recomendable usarlo como clave. Lo ideal sería utilizar el número del ítem, es decir, su posición en el array. Para eso, modificamos el v-for
como se ve a continuación:
<template>
<div>
<div v-for="(item, index) in array" :key="index">{{ index }}. {{ item }}</div>
</div>
</template>
Ahora si debería funcionarnos correctamente, ya que esto permite que en cada iteración item
tenga el valor del elemento (HTML, CSS, Javascript...) e index
tenga la posición en el array (0, 1, 2...). Finalmente, el navegador obtendría esto:
<div>
<div>0. HTML</div>
<div>1. CSS</div>
<div>2. Javascript</div>
<div>3. Terminal</div>
</div>
OJO: Ten en cuenta que los índices empiezan a contar en
0
. Si quieres que empiecen en uno, es tan fácil como escribir{{ index + 1 }}
.
Iterando otros tipos de datos
Con la directiva v-for
podemos iterar varios tipos de datos (en general cualquiera que sea iterable), pero si los esquematizamos tenemos los siguientes, los cuales cada uno tiene ciertas particularidades.
Por ejemplo, observa que al igual que con el .forEach()
podemos utilizar la sintaxis básica (si sólo usaremos el ítem) o la sintaxis compleja (si necesitamos, por ejemplo, el número de iteración):
Ejemplo | Sintaxis avanzada | Descripción |
---|---|---|
v-for="item in array" | (item, index) | Iteramos por cada uno de los elementos del array. |
v-for="item in object" | (item, name, index) | Iteramos por cada una de las propiedades del objeto. |
v-for="i in number" | (item, index) | Iteramos en un bucle de 1 al número number . |
v-for="char in string" | (item, index) | Iteramos por cada carácter del string. |
Hasta ahora hemos visto sólo el de los item
, el nombre de la propiedad (name
) y su índice (index
).
Si en lugar de un v-for
iterará por cada uno de los carácteres que forma el string, como si fuera un array de carácteres. Lo cual también puede tener casos de uso interesantes.
Por último, en el caso de los
<template>
<div>
<div v-for="i in 10" :key="i">{{ i }} - {{ array[i] }}</div>
</div>
</template>
<script>
export default {
name: "BaseBlock",
data() {
return {
array: ["HTML", "CSS", "Javascript", "Terminal"],
};
},
};
</script>
Podemos reemplazar v-for="i in 10"
por v-for="(i, index) in 10"
y en este caso tendremos una variable i
que itera de 1..10
y una variable index
que itera de 0..9
. Esto puede ser útil para ciertas operaciones o bucles específicos.
No mezclar v-for con v-if
Hay que tener mucho cuidado a la hora de utilizar directivas v-for
en combinación con directivas v-if
, puesto que no son compatibles. Si lo intentamos, obtendremos este error del linter:
The array variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if'
Hay dos formas sencillas de solucionarlo:
- Añadir un etiqueta dentro/fuera del
v-for
con elv-if
y así evitar mezclarlos (más simple). - Crear una propiedad computada que filtre los resultados (más eficiente/reutilizable).
Imaginemos que queremos mostrar sólo los resultados impares de array
. La primera solución podría ser algo similar a esto:
<div v-for="(item, index) in array" :key="item">
<div v-if="index % 2 === 0">{{ index }}. {{ item }}</div>
</div>
La segunda solución, un poco más compleja pero quizás algo más limpia, podría ser crear una propiedad computada. Esto nos permitiría gestionarlo a través de Vue y reutilizarlo si hay más casos en las que necesitemos la información filtrada. Quedaría algo similar a esto:
<template>
<div>
<div v-for="(item, index) in filteredArray" :key="item">
{{ index }}. {{ item }}
</div>
</div>
</template>
<script>
export default {
name: "BaseBlock",
data() {
return {
array: ["HTML", "CSS", "Javascript", "Terminal"],
};
},
computed: {
filteredArray() {
return this.array.filter((item, index) => index % 2 === 0);
},
},
};
</script>
Es importante darse cuenta que la propiedad computada filteredArray
obtiene el array original y devuelve uno donde se filtran los ítems que nos interesan. Estos valores quedarán cacheados por Vue y se reutilizarán sin volverse a calcular, siempre que el array original no cambie.