1

I have a Vue 3.0 app. In this app, I have the following:

export default {
  data() {
    return {
      selected: null,
      departments: [
        { 
          name: 'Sports', 
          items: [
            { id:'1', label: 'Football', value:'football' },
            { id:'2', label: 'Basketball', value:'basketball' },
          ]
        },

        {
          name: 'Automotive',
          versions: [
            { id:'3', label: 'Oil', value:'oil' },
            { id:'4', label: 'Mud Flaps', value:'mud-flaps' },
          ]
        }
      ]
    };
  }
};

I need to render these items in a dropdown list. Notably, the items need to be rendered by sections. In an attempt to do this, I'm using Bootstrap's dropdown headers. My challenge is, the HTML needs to be rendered in a way that I can't see how to do with Vue. I need to render my HTML like this:

<ul class="dropdown-menu" aria-labelledby="dropdownMenuLink">
  <li><h6 class="dropdown-header">Sports</h6></li>
  <li><a class="dropdown-item" href="#">Football</a></li>
  <li><a class="dropdown-item" href="#">Basketball</a></li>
  <li><h6 class="dropdown-header">Automotive</h6></li>
  <li><a class="dropdown-item" href="#">Oil</a></li>
  <li><a class="dropdown-item" href="#">Mud Flaps</a></li>
</ul>

Based on this need, I would have to have a nested for loop. In psuedocode, it would be like this:

foreach department in departments
  <li><h6 class="dropdown-header">{{ department.name }}</h6></li>
  foreach item in department.items
    <li><a class="dropdown-item" href="#">{{ item.label }} </a></li>
  end foreach
end foreach

I know I could do this with something like Razer. However, I don't see anyway to do this in Vue. Yet, I have to believe I'm overlooking something as rendering a hierarchy is a common need. How do you render a hierachy in Vue without changing the data structure?

Thank you.

4 Answers 4

3

just use nested v-for like this:

<template v-for="(department , i) in departments">
    <li><h6 class="dropdown-header" :key="`head-${i}`">{{ department.name }}</h6></li>
    <template v-for="(item , j) in department.items">
        <li><a class="dropdown-item" href="#" :key="`sub-${i}${j}`">{{ item.label }} </a></li>
    </template>
</template>
Sign up to request clarification or add additional context in comments.

Comments

1

you can have nested v-for with the help of template and also make sure that the keys binding to the elements are unique (this has to be unique so you don't get weird behavior from vue).

run the code below and check the result (click on full screen to see the full list rendered):

// you can ignore this line
Vue.config.productionTip = false;

new Vue({
  el: '#app',
  data: {
    departments: [{
        name: 'Sports',
        items: [{
            id: '1',
            label: 'Football',
            value: 'football'
          },
          {
            id: '2',
            label: 'Basketball',
            value: 'basketball'
          },
        ]
      },

      {
        name: 'Automotive',
        items: [{
            id: '3',
            label: 'Oil',
            value: 'oil'
          },
          {
            id: '4',
            label: 'Mud Flaps',
            value: 'mud-flaps'
          },
        ]
      }
    ],
  },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <ul class="dropdown-menu" aria-labelledby="dropdownMenuLink">
    <template v-for="({name, items}, i) in departments">
      <li :key="`header-${i}`">
        <h6 class="dropdown-header">{{ name }}</h6>
      </li>
      <li v-for="{label, id} in items" :key="`link-${id}`">
        <a class="dropdown-item" href="#">{{ label }}</a>
      </li>
    </template>
  </ul>
</div>

Comments

0

One approach to this is to use a computed method. I've created a jsfiddle here.

computed: {
  departmentItems() {
    const items = [];
    this.departments.forEach(department => {
      items.push({
        label: department.name,
        class: 'dropdown-header',
      });

      department.items.forEach(item => {
        items.push({
          label: item.label,
          class: 'dropdown-item'
        })
      })
    })

    return items;
  }
}

The important part here is, that we have a common model for both dropdown-header and dropdown-item.

Comments

0

You structure your data in a computed property, and then use v-for to iterate over the list. Here is an example:

new Vue({
  el: "#app",
  data() {
    return {
      selected: null,
      departments: [
        { 
          name: 'Sports', 
          items: [
            { id:'1', label: 'Football', value:'football' },
            { id:'2', label: 'Basketball', value:'basketball' },
          ]
        },
        {
          name: 'Automotive',
          items: [
            { id:'3', label: 'Oil', value:'oil' },
            { id:'4', label: 'Mud Flaps', value:'mud-flaps' },
          ]
        }
      ]
    }
  },
  computed: {
    departmentsItem: function() {
      return this.departments.reduce((acc,department) =>
        [ 
          ...acc, 
          { type: "department", name: department.name },
          ...department.items.map(item => ({ type: "item", name: item.label }))
        ]
      , []);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <ul class="dropdown-menu" aria-labelledby="dropdownMenuLink">
    <li v-for="(departmentItem,i) in departmentsItem" :key="i">
      <h6 
        v-if="departmentItem.type==='department'" class="dropdown-header"
      >{{departmentItem.name}}</h6>
      <a 
        v-else-if="departmentItem.type==='item'" class="dropdown-item" href="#"
      >{{departmentItem.name}}</a>
    </li>
  </ul>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.