Vue.js - 嵌套的 v-for 循环

时间:2021-03-07 13:56:47

标签: javascript vue.js

我有一个 Vue 3.0 应用。在这个应用程序中,我有以下内容:

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' },
          ]
        }
      ]
    };
  }
};

我需要在下拉列表中呈现这些项目。值得注意的是,项目需要按部分呈现。为了做到这一点,我使用了 Bootstrap's dropdown headers。我的挑战是,HTML 需要以一种我无法使用 Vue 的方式呈现。我需要像这样呈现我的 HTML:

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

基于这种需要,我必须有一个嵌套的 for 循环。在伪代码中,它会是这样的:

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

我知道我可以用雷蛇之类的东西来做到这一点。但是,我无论如何都看不到在 Vue 中这样做。然而,我不得不相信我忽略了一些东西,因为呈现层次结构是一种常见的需求。如何在不改变数据结构的情况下在 Vue 中渲染层次结构?

谢谢。

4 个答案:

答案 0 :(得分:2)

像这样使用嵌套的 v-for:

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

答案 1 :(得分:1)

您可以在 v-for 的帮助下嵌套 template,并确保 key 与元素的绑定是唯一的(这必须是唯一的,这样您就不会从 vue 中获得奇怪的行为)。

运行下面的代码并检查结果(点击全屏查看呈现的完整列表):

// 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>

答案 2 :(得分:0)

一种方法是使用计算方法。我创建了一个 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;
  }
}

这里的重要部分是,我们有一个用于下拉标题和下拉项目的通用模型。

答案 3 :(得分:0)

您在 computed 属性中构建数据,然后使用 v-for 遍历列表。下面是一个例子:

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>