Dynamic v-for loop in VueJS - vue.js

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>

Related

How to fix simple error in moving ".slice()" into setup() as a computed function?

I'm using Vuejs3 and trying to move .slice(0,3) (limits v-for to 3) into a computed function in the setup(), in order to limit the number of elements in a v-for (that happens to be nested in another v-for).
Everything works as intended before the change, using the following code:
<ul class="mt-4">
<div v-for="review in prime.allReviews" :key="review">
<li v-for="pro in review.propPros.slice(0, 3)" :key="pro">
{{ pro }}
</li>
</div>
</ul>
When I try to move the slice(0, 3) into the setup() as a computed function, per a dozen suggestions it's best practice to do so, the DOM goes blank. Here's the code causing the problem:
<ul class="mt-4">
<div v-for="review in prime.allReviews" :key="review">
<li v-for="pro in limitedPros" :key="pro">
{{ pro }}
</li>
</div>
</ul>
And in setup():
const limitedPros = computed(() => {
return review.propPros.slice(0, 3);
});
...
return {
limitedPros,
};
Thanks for any help!
Try to pass review to function, without computed property:
(When you want to loop review you need to know which one)
const { ref } = Vue
const app = Vue.createApp({
setup() {
const prime = ref({allReviews: [{propPros: [1,2,3,4,5]}, {propPros: [6,7,8,9]}]})
const limitedPros = (review) => {
return review.propPros.slice(0, 3);
};
return { limitedPros, prime };
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<ul class="mt-4">
<div v-for="review in prime.allReviews" :key="review">
<li v-for="pro in limitedPros(review)" :key="pro">
{{ pro }}
</li>
</div>
</ul>
</div>

List rendering in vue

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>

x-template has trouble displaying value on the v-for

I had this issue while trying to render html into a vue component.
I am trying to insert component html through x-template. The issue is when I was trying to display the value {{i.value}} like this it was throwing error on console.
<script type="text/x-template" id="first-template">
<div>
<ul>
<li v-for="i in dataCollection">{{ i.id }}</li>
</ul>
</div>
</script>
Vue.component('menu', {
template: '#first-template',
data() {
return {
dataCollection: [{"id":"01"}, {"id":"02"}, {"id":"03"}],
}
}
});
The error on console was:
But when I was giving value as attribute like:
<script type="text/x-template" id="first-template">
<div>
<ul>
<li v-for="i in dataCollection" :id="i.id"></li>
</ul>
</div>
</script>
it works perfect.
Anyone know any fix ?
You should not put script/x-template tages inside of the element that you mount to the main instance to. Vue 2.0 will read all of its content and try to use it as a template for the main instance, and Vue's virtualDOM treats script/x-template's like normal DOM, which screws everthing up,
Simply moving the template out of the main element solved the problem.
Source
This is a suggestion, not a answer.
As #DmitriyPanov mentioned, you'd better bind unique key when using v-for.
Another issue is you'd better to use non built-in/resevered html elements.
so change component id from menu to v-menu or else you like.
Then simulate similar codes below which are working fine.
I doubt the error is caused by some elements of dataCollection doesn't have key=id (probably you didn't post out all elements). You can try {{ 'id' in i ? i.id : 'None' }}.
Vue.component('v-menu', { //
template: '#first-template',
data() {
return {
newDataCollection: [{"id":"01"}, {"id":"02"}, {"id":"03"}, {'xx':0}],
dataCollection: [{"id":"01"}, {"id":"02"}, {"id":"03"}]
}
}
});
new Vue({
el: '#app',
data() {
return {testProperty: {
'test': '1'
}}
},
methods:{
test: function() {
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<v-menu></v-menu>
</div>
<script type="text/x-template" id="first-template">
<div>
<div style="float:left;margin-right:100px;">
<p>Old:</p>
<ul>
<li v-for="(i, index) in dataCollection" :key="index">{{ i.id }}</li>
</ul>
</div>
<div>
<p>Adjusted:</p>
<ul>
<li v-for="(i, index) in newDataCollection" :key="index">{{ 'id' in i ? i.id : 'None' }}</li>
</ul>
</div>
</div>
</script>
I think the problem here lies in the placement of the X-Template code (I had the same issue). According to the documentation:
Your x-template needs to be defined outside the DOM element to which Vue is attached.
If you are using some kind of CMS, you might end up doing just that.
What helped me in that case was (based on your example):
Placing the X-template script outside the #app
passing the collection as a prop to the v-menu component:
<v-menu v-bind:data-collection="dataCollection"></v-menu>
list dataCollection as a prop inside the v-menu component:
Vue.component('v-menu', { //
template: '#first-template',
props: [ "dataCollection" ],
...
});
I hope that helps anyone.
In 2.2.0+, when using v-for with a component, a key is now required.
You can read about it here https://v2.vuejs.org/v2/guide/list.html#v-for-with-a-Component

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