Using conditional Sub-Arrays using VueJS - vue.js

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

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>

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>

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.

How to add a div id to each element in a v-for list?

Here is my Vue
<div class="drag">
<h2>List 1 Draggable</h2>
<ul>
<li v-for="category in data">
<draggable v-bind:id="category" v-model="category" :move="checkMove" class="dragArea" :options="{group:'items'}">
<div v-for="item in category" style="border: 3px;">${ item.name }</div>
</draggable>
</li>
</ul>
</div>
</div>
<script>
var vm = new Vue({
el: "#main",
delimiters:['${', '}'],
data: {{ data | tojson | safe }},
methods:{
checkMove: function(evt){
return true }
}
});
I want each of the div in my v-for to have an id. Based on this; https://v2.vuejs.org/v2/guide/list.html
I think I need something like v-bind:key="category.id" in the li tag (the one with the v-for. That makes my div id [Object object][Object object][Object object].
I think this is for each of the items in my category. I would like to add a div id to each category as well as a div id to each item in category. These should be something like "category.name" (the name of the category, so uncategorized, and "item.name" in the item itself. My data object looks like this:
{"data": {"uncategorized": [{"id": 0, "name": ""}, {"id": 1, "name": "first"}, {"id": 2, "name": "another"}]}}
I solved this using v:bind-id or :id for short. Here is my full code:
<ul>
<li class='category-item' v-for="(object,category) in data">
<h1>${ object.name }</h1>
<a style id='add-item-btn' v-bind:href="'/create/'+object.category_id"><img width='10' src="{{ url_for('static',filename='images/add-item.png') }}"/></a>
<draggable :id="'category-' + object.category_id" v-model="object.items" :move="checkMove" class="dragArea" :options="{group:'items'}">
<div class="list-item" v-for="(item,key) in object.items" :id="item.id">
<a v-bind:href="'/edit/'+item.id"> ${ item.name }</a>
</div>
</draggable>
</li>
</ul>
If a data value is {uncategorized: [....]}, you want object v-for, which can give you the value part (the array, here) in the first associated variable and the key ('uncategorized') in the second associated variable. That's the category name you want, if I understand correctly. So maybe:
<li v-for="items, category in data">
<draggable :id="category" :key="category" v-model="category" :move="checkMove" class="dragArea" :options="{group:'items'}">
<div v-for="item in items" :id="item.name" style="border: 3px;">${ item.name }</div>
</draggable>
</li>
That would bind the category name as the id of the draggable, and the item name as the id of the inner div. Binding to key is a hint Vue uses in doing its update magic.
I don't know what you want to use in the v-model, but since you didn't ask about it, I assume you know what to do.

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