The goal is to make the output look like this:
<div id="tabs">
<div id="first">
<a>tab 1</a>
<a>tab 2</a>
</div>
<div id="second">
<a>tab 3</a>
</div>
</div>
Currently I'm using this solution (using two v-for loops):
tabs.js (current)
export default {
data() {
return {
tabs: {
first: [{ name: 'tab1' }, { name: 'tab2' }],
second: [{ name: 'tab3' }],
}
}
}
template: `
<div id="tabs">
<div id="first">
<a v-for="tab in tabs.first">{{ tab.name }}</a>
</div>
<div id="second">
<a v-for="tab in tabs.second">{{ tab.name }}</a>
</div>
</div>
`
}
I had an idea to do something like this but it performs more iterations than in the case with two loops:
tabs.js (idea)
export default {
data() {
return {
tabs: {
test: [
{ name: 'tab1', category: 'first' },
{ name: 'tab2', category: 'first' },
{ name: 'tab3', category: 'second' }
]
}
}
}
template: `
<div id="tabs">
<div v-for='category in ["first", "second"]' :id='category' :key='category'>
<template v-for="tab in tabs.test">
<a v-if="tab.category === category">{{ tab.name }}</a>
</template>
</div>
</div>
`
}
I read this topic but it contains slightly different solutions, which unfortunately didn't work in this case.
CodePudding user response:
There's no problem using more than one v-for
loops. And there's no problem using nested v-for
loops.
The problem I see with your current code is that it's not scalable. You're hard-coding the exact values of your tabs in <template />
(e.g: first
, second
).
The main idea here is to loop through tabs and, inside each tab, to loop through each contents, without the <template>
needing to know what the tab is or how many there are.
So that when you change your tabs to, say...
{
tab1: [{ name: 'intro'}],
tab2: [{ name: 'tab2-1' }, { name: 'tab2-2' }],
tab3: [{ name: 'tab3' }]
}
template still works, without needing any change.
To achieve this type of flexibility, you need to use a nested v-for
loop:
<div id="tabs">
<div v-for="(items, name) in tabs" :key="name" :id="name">
<a v-for="(item, key) in items" :key="key" v-text="item.name"></a>
</div>
</div>
Demo:
new Vue({
el: '#app',
data: () => ({
tabs: {
tab1: [{
name: 'intro'
}],
tab2: [{
name: 'tab2-1'
}, {
name: 'tab2-2'
}],
tab3: [{
name: 'tab3'
}]
}
})
})
#tabs a { padding: 3px 7px }
<script src="https://v2.vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="tabs">
<div v-for="(links, name) in tabs" :key="name" :id="name">
<a v-for="(link, key) in links"
:key="key"
:href="`#${link.name}`"
v-text="link.name"></a>
</div>
</div>
</div>
But I'd take it one step further and change the tabs
to be an array:
data: () => ({
tabs: [
[{ name: 'intro'}],
[{ name: 'tab1' }, { name: 'tab2' }],
[{ name: 'tab3' }]
]
})
And use :id="'tab-' name"
on tab divs if you really need those unique ids. (Hint: you don't).
It makes more sense to me.
CodePudding user response:
I did not see any harm in using two v-for
(one for the object keys and another for the array elements) as far as it is all dynamic. You can give a try to this solution by using of Object.keys()
:
new Vue({
el: '#app',
data: {
tabs: {
first: [{ name: 'tab1' }, { name: 'tab2' }],
second: [{ name: 'tab3' }],
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="tabs">
<div v-for="tab in Object.keys(tabs)" :key="tab" :id="tab">
<a v-for="tab in tabs[tab]">{{ tab.name }}</a>
</div>
</div>
</div>
CodePudding user response:
You could add a computed property being the .concat
from both and loop for it
export default {
data() {
tabs: {
first: [{ name: 'tab1' }, { name: 'tab2' }],
second: [{ name: 'tab3' }],
}
},
computed: {
tabsCombined () {
return this.tabs.first.concat(this.tabs.second)
}
},
template: `
<div id="tabs">
<div v-for='category in tabsCombined' :id='category' :key='category'>
<template v-for="tab in tabs.test">
<a v-if='tab.category === category>{{ tab.name }}</a>
</template>
</div>
</div>
`
}