vuejs v-for execution difference with or withouth sub-components - vue.js

consider you got a list of objects you want to iterate over of which each object contains an array with the name f. Also you got a log function that prints a string from a parameter and returns true. Here you see the corresponding template section:
<template>
<div>
<div v-for="o in objArr" v-if="log('base')">
<div v-for="a in o.f" v-if="log('subbase')">
</div>
</div>
</div>
</template>
Here is an example of the data if have used:
objArr: [
{ a : 1, b : 2, c : 3, d : 4, e : 5, f : [10,9,8]},
{ a : 1, b : 2, c : 3, d : 4, e : 5, f : [10,9,8]},
],
Now I change a random array element in one of the Objects. I do this on the console with:
$vm.objArr[3].f.splice(2,1,4);
(This will change the 4th Object, and within that object replace the 3rd array element with the value of 4).
Because of vue's reactive capabilities, it will detect the change and rerender the necessary components. Imagine the objArr has 2 elements and the array of object we are changing has 3 elements. The Output will be:
base
subbase
subbase
subbase
base
subbase
subbase
subbase
Now I want to use components as wrappers. I change above to:
<div>
<div >
<performance-test v-for="o in objArr" v-if="log('base')" :mydata="o">
</performance-test>
</div>
</div>
And inside the performance-test component we got:
<div>
<div v-for="a in mydata.f" v-if="log('basesub')">
</div>
</div>
We still got a for-loop inside a for-loop and I use the same method as above to change the base components array, but vue decides to update different this time. The output is:
subbase
subbase
subbase
I can't make up my mind right now. If I don't want to have any data (and logic) in my subcomponents. How would I prevent vue from going all over the whole object even if only one array inside a single object changed?
Hope you can help.

It seems the problem is you have not declared a :key when using performance-test (a custom component) in a v-for. Try:
<performance-test v-for="o in objArr" v-if="log('base')" :mydata="o" :key="o.a">
</performance-test>
Note: o.a must be unique for each o!
Codesandbox working demo here.

Related

Limit result on a v-for with vueJS [duplicate]

This question already has answers here:
How to limit iteration of elements in `v-for`
(7 answers)
Closed 2 years ago.
Simple problem, i have a members list where i iterate through it with a v-for.
How can i limit the results and only show the first 2 ?
members = [ {id : 1, name: 'Franck'}, {id : 2, name: 'Sophie'}, {id : 3, name: 'Bob'}]
<div v-for="member in members" :key="member.id">
<p>{{ name }}</p>
</div>
Just want to know if it's feasible from template ? Otherwise i know that i can use a computed properties that filtered it and i just have to loop through the results of my filtered array
You can use slice() in the template if you prefer to not use a computed property. I would though choose to have a computed property, if for nothing else, I like to handle all logic in script instead of template. But as said, you can use slice:
v-for="(member, index) in members.slice(0, 2)"
A forked fiddle that was provided by #Raffobaffo in a comment.
After some discussions here, lets divide the answer in two:
A fast, not super correct way but still referred inside vue docs
Use a v-if and set and index in the v-for.
<div v-for="(member, index) in members" :key="member.id" v-if="index < 3">
<p>{{ name }}</p>
</div>
else, the most correct way is to create a computed property returning just the elements you need:
<template>
<div v-for="member in justTwoElements" :key="member.id">
<p>{{ name }}</p>
</div>
<template>
....
computed: {
justTwoElements: function(){
return this.members.slice(0,2);
}
}
This latest solution is more indicated when your data entry is huge, the benefits of using it instead of the first solution are well explained here.
I hope this helps to give you the correct path to follow

Assign index of v-for to id of created component

I am creating components in a loop in my vue app. However, I need those components to have an id value like "board-1" etc. using the index of the loop. (Just like I did it with v-bind:key="component-${block._uid}".)
How can I achieve this?
<div
class = "col-4"
v-for="(block, index) in layouts"
v-bind:key="`component-${block._uid}`"
>
<Board
id="`board-${block._uid}`"
class="droparea"
#dropped-component="$emit('dropped-component', $event, index)"
:acceptsDrop=true
draggable="true"
>
Layout {{index + 1}}
</Board>
</div>
You need to bind the value for JS code to get executed, otherwise you are attributing a string, not JS code.
:id="`board-${block._uid}`"
You can also use v-bind, actually : is just a shorthand
v-bind:id="`board-${block._uid}`"

Access next index object's value within a loop - Vue JS

I wanted to check for a condition if a particular property of an object is same as the very next object's property in an array-of-objects, iterated through a v-for loop.
Sample JSON object:
[
{ Date: '21-July-2017', ...},
{ Date: '21-July-2017', ...},
{ Date: '25-July-2017', ...},
...
]
The requirement is to check if each consecutive Date value is same, so that we will hide the Date header in the UI.
<div v-for="(everyDay, dayIndex) in eachPost.values" v-bind:key="dayIndex">
<div v-if="everyDay['Date'] !== eachPost.values[dayIndex+1].Date">
THIS DOESN'T WORK
</div>
</div>
Is there an alternative to accomplish this req?
Your problem is when you get to your last item in the array your dayIndex+1 object does not exist. It is undefined. What you need to do is in your template determine if your object is defined and go from there.
<div v-for="(everyDay, dayIndex) in eachPost.values" v-bind:key="dayIndex">
<template v-if="eachPost.values[dayIndex+1]">
<div v-if="everyDay['Date'] !== eachPost.values[dayIndex+1].Date">
THIS WORKS
</div>
</template>
</div>
Here is a jsFiddle of my working example

Vue Js define Iterator

How can we define new iterator like v-for (for example v-for2) that iterate over an array ?
I want to accumulate one property of array items.
the result would be like this :
<div v-for2="(item, accumulated) in [1,2,3,4,5,6]">
{{accumulated}}
</div>

Vue.js / vuedraggable : Adding "v-model" to a draggable makes it not draggable

I have a problem using Vue.js and the library VueDraggable (https://github.com/SortableJS/Vue.Draggable)
Here is a sample of code :
<draggable v-if="n === 2 && track.getDisplayArray()[1].length !==0"
element="ul"
:options="{group:'layers '+ track.itemId}"
v-bind:class="{ hidden: !track.show, 'child-container': track.show }"
v-model="track.getDisplayArray()[1]"
:move="onMove"
#start="onStart"
#end="onFinished">
<li class="item" v-bind:class="{ hidden: !layer.show, child: layer.show }"
v-for="layer in track.getDisplayArray()[1]">
<span>{{layer.name}}</span>
<img class="item-view" src="static/img/ic_view.svg" alt="view">
</li>
</draggable>
onMove function just returns true, onStart and onFinished are empty (but I want to do something with them in the future ;) )
When the "v-model" property is here, the li tags which are created cannot be swapped.
When I remove this property, the li tags can be swapped.
Do you see the problem? Are they some "conflicts" between some properties that I am not aware of?
I found the solution of my problem. Changing v-model to value didn't change anything, because they don't see updates from the list they are linked to.
I replaced the "v-model" property by the property "list" .
According to the documentation :
The main difference is that list prop
is updated by draggable component using splice method, whereas value
is immutable.