List rendering in vue - vue.js

I'm running through a list of cart items in vue and need to get the index of each item. I thought you'd simply do something like this
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem, index) in lineItems" :key="lineItem.key">
<PushCartItem :lineItem="lineItem" />
</li>
</ul>
And
<p class="crt-PushItem_Price"><span class="money" data-line-index="{{ index }}">{{ formatMoney(lineItem.line_price) }} </span></p>
But this isn't displaying and I'm getting the error
[Vue warn]: Property or method "index" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

I'm assuming that this line
<p class="crt-PushItem_Price"><span class="money" data-line-index="{{ index }}">{{ formatMoney(lineItem.line_price) }} </span></p>
is the PushCartItem component, so in order to receive the index property, you need to use props, because otherwise you can't have access to it, like this:
<template>
<p class="crt-PushItem_Price">
<span class="money" :data-line-index="index">
{{ formatMoney(lineItem.line_price) }}
</span>
</p>
</template>
<script>
export default {
name: 'PushCartItem',
props: {
index: {
type: Number,
required: true
},
lineItem: {
type: Object,
required: true,
}
},
methods: {
formatMoney(price) {
// do something with the price
}
}
}
</script>
This answer is based in my assumptions, if you still having the error, please give me more information or details about it to help you.

You need to pass the index from the parent component like this.
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem, index) in lineItems" :key="lineItem.key">
<PushCartItem :lineItem="lineItem" :index="index" />
</li></ul>

Try this:
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem) in lineItems" :key="lineItem.key">
<PushCartItem :lineItem="lineItem" />
</li>
</ul>
Or this:
<ul class="crt-Push_Items">
<li class="crt-Push_Item" v-for="(lineItem, index) in lineItems" :key="index">
<PushCartItem :lineItem="lineItem" />
</li>
</ul>

Related

Dynamic v-for loop in VueJS

I have to yml files (one in Fr and another in Nl), each with a line called "language", with the respective language.
Let's say that I'm receiving from the back a list in both languages of words, I save them as listFr & listNl.
And what I want to do is a dynamic v-for loop, using the variable language mentioned above, maybe something like this
<ul>
<li
v-for="(list, index) in `list${$t('language')}`"
:key="index"
>
<p class="element-title">{{ list.title }}</p>
</li>
</ul>
But obviously this is not correct as it's not showing the list of words.
Thanks for your help!
I think this is the best way
<template>
<ul>
<li v-for="(list, index) in dynamicList" :key="index">
<p class="element-title">{{ list.title }}</p>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
listFr: [],
listNi: [],
}
},
computed: {
dynamicList() {
return this["list" + this.$t('language')]
}
}
};
</script>

Vuejs v-for loop with tag combination

I want to loop two list tags for every loop of v-for without looping ul. Is there any internal looping available in vuejs to escape the ul tag by getting looped or is there any other method.
<ul id="example-1" v-for="(item, index) in items" :key="item.message">
<li >
{{ item.message }}
</li>
<li>
{{ item.text}}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo', text: "baz" },
{ message: 'Bar', text: "quz" }
]
}
})
The obvious result that i can get is :
Foo
Bar
baz
quz
The result That i need:
Foo
baz
Bar
quz
You can loop on a <template> tag.
<ul id="example-1">
<template v-for="item in items">
<li>
{{ item.message }}
</li>
<li>
{{ item.text}}
</li>
</template>
</ul>
If you are using Vue <= 2.x, then you will need to assign keys to the inner elements, since templates cannot be keyed.
If you are using Vue 3.x, then you should assign a key to the <template>. This is a breaking change from earlier releases.

Class and Style binding on certain conditions

I'm making a navigation component with nuxt.js, vue and storyblok. In this navigation i have a few items. i'm getting my navi items out of the storyblok API. i want to style certain navi items differently when the 'highlighted' attribute in the API equals to true or false.
My problem is that i dont exactlty know how to do that. this is what i have at this point.
div in my navigation component:
<div v-if="items" class="main-nav">
<nav>
<ul>
<li v-bind:class="{ highlighted: item.highlighted === isHighlighted, not_highlighted: item.highlighted === isNotHighlighted}" v-editable="items" :key="index" v-for="(item, index) in items">
<LinkType class="nav-link" :link="item.link" :linkText="item.name">{{ item.name }}</LinkType>
</li>
</ul>
</nav>
</div>
this is how i retrieve my data:
data() {
return {
items: this.$store.state.settings.main_nav ? this.$store.state.settings.main_nav : [],
isHighlighted: true,
isNotHighlighted: false
}
}
whenever i try to console.log item.hightlighted it gives back an undefined error. i would appreciate some help.
Try as below =>
<div v-if="items" class="main-nav">
<nav>
<ul>
<li v-bind:class="item.highlighted === isHighlighted ? 'highlighted' : 'not_highlighted'" v-editable="items" :key="index" v-for="(item, index) in items">
<LinkType class="nav-link" :link="item.link" :linkText="item.name">{{ item.name }}</LinkType>
</li>
</ul>
</nav>
</div>
You can also remove isNotHighlighted: false from data.

Using conditional Sub-Arrays using VueJS

I would like to know how to use Vuejs to output a conditional sublist. In this example, not all topics have a subtopic.
new Vue ({
el: '#maincontainer',
data: {
topics: [
{ topicname: 'Introduction' },
{ topicname: 'First Chapter',
subtopics: [
{subtopicname: 'Test'}
]
},
]
}
});
My HTML so far looks like this:
<li v-for="topic in topics" id="item-{{$index}}">
{{ topic.topicname }}
<ul>
<li v-for="subtopic in subtopics">
{{ subtopic.subtopicname }}
</li>
</ul>
</li>
How might I have the list optionally add a sublist if there is one in the data?
v-if is the directive to be used for conditional rendering.
Here you could use v-if='topic.subtopic' too (as long as the value of the expression evaluates to a truthy boolean value if subtopic existed.)
<li v-for="topic in topics" id="item-{{$index}}">
{{ topic.topicname }}
<ul v-if='Array.isArray(topic.subtopic)'>
<li v-for="subtopic in topic.subtopics">
{{ subtopic.subtopicname }}
</li>
</ul>
</li>
You might also be interested in v-else

Skip object items if the value is null

I have a nested for ... in loop in vue js. What I'm trying to to is to skip elements if the value of the element is null. Here is the html code:
<ul>
<li v-for="item in items" track-by="id">
<ol>
<li v-for="child in item.children" track-by="id"></li>
</ol>
</li>
</ul>
null elements may be present in both item and item.children objects.
For example:
var data = {
1: {
id: 1,
title: "This should be rendered",
children: {
100: {
id: 100,
subtitle: "I am a child"
},
101: null
}
},
2: null,
3: {
id: 3,
title: "Should should be rendered as well",
children: {}
}
};
With this data data[1].children[101] should not be rendered and if data[1].children[100] becomes null later it should be omitted from the list.
P.S. I know this is probably not the best way to represent data but I'm not responsible for that :)
Edit: Actually, a simple v-if might work:
<li v-for="item in items" v-if="item !== null" track-by="id">
Give it a try. If not, do this:
You can add a filter for that (in main.js before App instance):
Vue.filter('removeNullProps',function(object) {
// sorry for using lodash and ES2015 arrow functions :-P
return _.reject(object, (value) => value === null)
})
then in the template:
<li v-for="item in items | removeNullProps" track-by="id">
<ol>
<li v-for="child in item.children | removeNullProps" track-by="id"></li>
</ol>
</li>
In Vue 2, filters have been deprecated in v-fors.
Now you should use computed properties. Demo below.
new Vue({
el: '#app',
data: {
items: [
'item 1',
'item 2',
null,
'item 4',
null,
'item 6'
]
},
computed: {
nonNullItems: function() {
return this.items.filter(function(item) {
return item !== null;
});
}
}
})
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
Using a computed property:
<ul>
<li v-for="item in nonNullItems">
{{ item }}
</li>
</ul>
<hr>
Using v-if:
<ul>
<li v-for="item in items" v-if="item !== null">
{{ item }}
</li>
</ul>
</div>
I would advise you against using v-if and v-for in the same element. What I found worked and didn't affect performance is this :
<li v-for="(value, key) in row.item.filter(x => x !== null)" :key="key"{{value}}</li>
You just need to run the filter function on the array you are going through. This is a common use in c# and found it was no different in JavaScript. This will basically skip the nulls when iterating.
Hope this helps (3 years later).
Just use v-if to do with it. But the first, do not use track-by="id" because of the null item and null child. You can check the demo here https://jsfiddle.net/13mtm5zo/1/.
Maybe the better way is to deal with the data first before the render.
VueJs style guide tell us to :
"Never use v-if on the same element as v-for."
How to handle v-if with v-for properly according to the vue style guide :
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
See more on how to handle v-if with v-for here : https://v2.vuejs.org/v2/style-guide/#Avoid-v-if-with-v-for-essential