简体   繁体   中英

vue.js - Is there any way to render elements in two different divs using one v-for loop?

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.

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>

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 /> (eg: 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.

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>
    `
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM