This directive is used for looping. Consider the following example -
<ul> <li v-for="n in 10">{ { n }}</li> </ul>
So v-for
is used on an element. If the v-for
can iterate n
times, it will create n
number of same element. In the above example n
is the variable in which we want to store each item. By specifing integer it will start iterating from 1
and will continue until the integer number comes and iterate.
You can also provide an array. Consider the following --
data: { names : ['Santanu', 'Atanu', 'Sumita', 'Aloke'] } <ul> <li v-for="name in names">{ { name }}</li> </ul>
You can also have an array of object. Consider the following --
data:{ names : ['Santanu', 'Atanu', 'Sumita', 'Aloke'], Students : [ { name : 'Santanu Bera', roll : 20 }, { name : 'Manik Sen', roll : 10 } ] } <ul> <li v-for="student in Students"> Name : { { student.name }}, Roll No : { { student.roll }} </li> </ul>
Instead of using { { }}
brackets, you can use v-text
or v-html
depending on your test and requirements. The above example can be replaced with the following -
<li v-for="student in Students" v-html="'Name :' + student.name + ', Roll No : ' + student.roll "> </li>
Inside v-for blocks we have full access to parent scope properties. v-for also supports an optional second argument for the index of the current item.
var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ] } }); <ul id="example-2"> <li v-for="(item, index) in items"> { { parentMessage }} - { { index }} - { { item.message }} </li> </ul>
We are using in
operator within the v-for
syntax. But we can also use of
operator instead of in
operator. And that will make it closer to Pure JS syntax. But there's no different between these two. It's only user's taste of choice.
<div v-for="item of items"></div>
You can use object too.
<ul> <li v-for="item in Students[0]">{ { item }}</li> </ul>
You can also provide key
as the second argument. In the following example, the first argument is value and the second argument is key.
<ul> <li v-for="(theValue, theKey) in Students[0]"> { { theKey }} : { { theValue }} </li> </ul>
You can also provide the index
as the third argument -
<ul> <li v-for="(theValue, theKey, i) in Students[0]"> { { i }}. { { theKey }} : { { theValue }} </li> </ul>
It is always recomended to use key
attribute with the element whenever possible. Because internally how Vue works, using key will make it render the element efficiently and correctly.
A key
is the unique string to identify element or component.
<ul> <li v-for='(item, index) in items' :key="index">{ { item }}</li> </ul>
Sometimes you want to change the array dynamically and also update the rendering when you are using v-for. Well, Vue does take cares of all the thing. You just update the array and vue dynamically and instantly will update the DOM according to the updated array. Consider the following example -
data : { items : ['Pear', 'Date', 'Coconut'], writeItem : "" }, methods:{ addItem:function(){ if(this.writeItem!="") this.items.push(this.writeItem); this.writeItem = ""; } } <input type="text" v-model="writeItem"> <button type="button" v-on:click="addItem"></button> <ul> <li v-for='(item, index) in items' :key="index">{ { item }}</li> </ul>
The above code will output the following -
In the above demo you can see how it works. Everytime you push an item to the array, Vue updates the DOM. Pretty cool. You can do much more than this.
The above example shows the mutation method push
. But there are other function which you can also apply. Like pop()
, shift()
, unshift()
, slice()
, sort()
, reverse()
etc.
There are other methods that doesn't update the original array. Rather, the take the copy of original array and return a new array. For example, slice()
method. When working with non-mutating methods, you can replace the old array with the new one.
this.items = example1.items.filter(function (item) { return item.message.match(/Foo/) })
In the above example, we are returning a new array and assigning it to. You might think this will cause Vue to throw away the existing DOM and re-render the entire list - luckily, that is not the case. Vue implements some smart heuristics to maximize DOM element reuse.
When working with v-for
there are certain thing you should remember.
When you directly set an item with the index, e.g. this.items[indexOfItem] = newValue; Vue cannot track the update in the array and you won't see any change. So avoid modify the array directly.
Always use method like push()
, slice()
etc to update the array. Or, you can go with the following -
It takes three argument - target
, key
and value
. And returns the value that has been set. This method set a property on an object. If the object is reactive, ensure the property is created as a reactive property and trigger view updates. This is primarily used to get around the limitation that Vue cannot detect property additions.
Vue.set(target, key, value);
We generaly always update the array using methods, there's no situation when we need to modify the array using index way or neither do we need to update the length directly. That's a bad idea. But for Object type in JS, we generally update object using the syntax like obj[key] = value;
. But due to limitatio of JS, Vue cannot detect the change, that's why Vue.set()
is used to set a new key to the object so that Vue can update the DOM accordingly.
var vm = new Vue({
data: {
a: 1 // The property a is reactive
}
});
What if you want to another property to data object from inside the Vue instance or outside the vue whatever. Doing it like the following?
vm.b = 2 //The property b is not reactive
The above will surely add the key b
to the data, but it won't be reactive. You can also directly access it using vm.b
and it will return 2
, but if you do vm.$data
, you won't see any property called b
. So this not the proper way to assign a new property to data object. Infact there's no way. Because the following won't work properly -
Vue.set(vm.$data, "name", "Santanu");
Try the above code in the console it will output error. Vue won't like it. Because Vue doesn't allow root instance(vm) or data property (vm.$data) to be passed in the method Vue.set()
. It can take any object except this two.
As Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method. For example, given:
var vm = new Vue({ data: { userProfile: { name: 'Anika' } } })
You could add a new age property to the nested userProfile object with:
Vue.set(vm.userProfile, 'age', 27);
You can also use the vm.$set instance method, which is an alias for the global Vue.set:
vm.$set(this.userProfile, 'age', 27);
What if you want to delete an object instead of adding. Using a delete
operator? Well, once again, Vue cannot detect it and cannot update the DOM accordingly. That's why similarly to Vue.set(), Vue provides Vue.delete()
to delete a key on an object.
Vue.delete(target, key);
The above methoed deletes a property on an object. If the object is reactive, ensure the deletion triggers view updates. This is primarily used to get around the limitation that Vue cannot detect property deletions, but you should rarely need to use it.
In the above example, target is the target object on which you want to perform the deletion and key is the key that exists in that object.
The target object cannot be a Vue instance, or the root data object of a Vue instance.
This is just an alias of Vue.delete() method.
Also don't assign the length property directly like this.items.length = 100;
You might already know that if you assign a length to an array that is less than the array's actual length, JS will trancate the array.
let arr = ['Apple', 'Banana', 'Guava', 'Pear', 'Date', 'Coconut']; arr.length = 3; log(arr); // ['Apple', 'Banana', 'Guava'];
But if you do it for an array that is reactive, Vue won't detect it that the array has changed. To get around with this problem you can use method splice()
. The above deletion operation is same as the following -
vm.items.splice(newLength);
Now the Vue will update the DOM according to the new change in the array.
Sometimes you may want to assign a number of new properties to an existing object, for example using Object.assign() or _.extend(). In such cases, you should create a fresh object with properties from both objects. So instead of:
Object.assign(this.userProfile, { age: 27, favoriteColor: 'Vue Green' });
You would add new, reactive properties with:
this.userProfile = Object.assign({}, this.userProfile, { age: 27, favoriteColor: 'Vue Green' });
Sometimes you may want to render multiple block of element using v-for. You can do this using div
as a wrapper like this -
<ul> <div v-for='(item, index) in items' :key="index"> <li>{ { item }}</li> <li class="divider"></li> </div> </ul>
This is fine but not logical. In the DOM, the ul
element will contain div and inside it li element. Which is not standard and may not work properly in certain situation. For these kind of scinario, you can use template
tag. This tag is built in tag comes with Vue.js. The above example can be replaced with the following -
<ul> <template v-for='(item, index) in items' :key="index"> <li>{ { item }}</li> <li class="divider"></li> </template> </ul>
Now if you look in the DOM, the Vue removes the template element. It doesn't render this element. Which is more convenient.
Sometimes you want to conditionally render the element. But some learner may get confused with the usage of v-if
along with v-for
. Consider the following example -
<li v-for="(item, index) in items" :key="index" v-if="age>18"></li>
Now the question is? which one of the following is true?
Learners get confused at first. The correct answer is the second one. v-if
will run on each iteration and check if it's true. If it is, then Vue will render the element, otherwise v-for will continue with the next iteration.
<li v-for="todo in todos" v-if="!todo.isComplete"> { { todo }} </li>
Okay, what if you want to achieve the first one where you want to iterate the v-for only if the condition is true?
The answer is template
tag. You can wrap the whole v-for block within the template tag with the v-if condition.
<template v-if="age>18"> <ul> <li v-for="(item, index) in items" :key="index">{ { item }}</li> </ul> </template>
You can use v-for
on a component directly just like the normal element. In this case using key is must in the new version of Vue.
<my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index" v-bind:key="item.id" ></my-component>