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

Time:07-09

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