【问题标题】:Multiple v-for loops in same vue file同一个 vue 文件中的多个 v-for 循环
【发布时间】:2020-03-12 03:37:48
【问题描述】:

我正在尝试使用我的 api 中的三个 for 循环来填充表。

  const events = await app.$axios.get('/api/events/')
  const markets = await app.$axios.get('/api/markets/')
  const markets = await app.$axios.get('/api/runners/')

我需要确保表行索引是 runners,因为 runners 列的跑步者比市场多,市场的行数比事件多。

像这样。

Event Market
           Runner
           Runner
           Runner
      Market
           Runner
           Runner   

当我尝试在同一个 vue 文件中执行多个 for for 循环时,我收到此错误。

duplicate attribute key

为什么在单独的循环中使用 id 会出现此错误? 我的问题是如何根据每行的跑步者索引填充表格?

这是我目前的代码。

  <template>
    <div class="course-list-row">
      <th style="width:5%"> Start date </th>
      <th style="width:5%"> Event name</th>
      <th scope="col">Market</th>
      <th scope="col">Runner</th>

      <tr v-for="(event, index) in events" :key=id
          v-for="(market, index) in markets" :key=id
          v-for="(runner, index) in runners" :key=id>
        <td>{{ event.start_time }} </td>
        <td>{{ event.event_name }} </td>
        <td>{{ market.name }} </td>
        <td>{{ runner.name }} </td>

        <td />
      </tr>
    </div>
  </template>

  <script>
    export default {

      async asyncData({
        app
      }) {

        try {
          const events = await app.$axios.get('/api/events/')
          const markets = await app.$axios.get('/api/markets/')
          const runners = await app.$axios.get('/api/runners/')

          return {
            events: events.data.results,
            markets: markets.data.results,
            runners: runners.data.results,
            error: false
          }
        } catch (e) {
          console.log('error', e)
          return {
            events: [],
            markets: [],
            runners: [],
            error: true
          }
        }
      },
    };

  </script>


  <style>
    th,
    td {
      font-family: ‘Lato’, sans-serif;
      font-size: 12px;
      font-weight: 400;
      padding: 10px;
      text-align: left;
      width: 0%;
      border-bottom: 1px solid black;

    }

  </style>

api 响应的 id 键如下。

/api/事件

{
  "count": 80,
  "next": null,
  "previous": null,
  "results": [{
    "id": 103,
    "sport_id": "9",
    "event_name": "Guilherme Clezar vs Uladzimir Ignatik",
    "start_date": "12-11-19",
    "status": "open",
    "event_id": 1273804660340017
  }]
}

/api/markets

{
  "count": 128,
  "next": "http://localhost:8000/api/markets/?page=2",
  "previous": null,
  "results": [{
    "id": 151,
    "market_id": 1273804670840017,
    "market_name": "Moneyline",
    "status": "open",
    "volume": 1107.5453,
    "event": 103
  }]
}

【问题讨论】:

  • No Runners 是最大的,然后是 Market 然后是 Events。
  • 1.您不能在同一个元素上有多个 v-for 属性,您需要为两个外部循环使用 &lt;template&gt; 元素。 2. id from :key=id 是在哪里定义的?您发布的代码中似乎不存在它。
  • 问题与错误完全一样 duplicate attribute key,您在同一元素中有 3 个 v-for,这是实际问题,您不能有超过 1 个同名属性
  • @skirtle 我是 javascript 和前端开发的新手。您是否有机会向我展示该问题的快速解决方案,以便我的循环填充数据?感谢您的 cmets。
  • 能否举例说明eventsmarketsrunners的内容

标签: javascript vue.js axios nuxt.js


【解决方案1】:

Key 用于在重新渲染时计算 DOM 的差异。键在父 DOM 节点的范围内必须是唯一的(我猜)。

尝试在您的密钥前加上一些东西。

v-for="(event, index) in events" :key="`event_${id}`" 

编辑: 实际上原因可能是您的 id 没有在任何地方定义。用索引替换它。

此外,您确定可以在同一个元素上使用三个循环吗?从未尝试过,我总是在计算道具中连接事物......有趣的方法!

【讨论】:

  • ${id} 来自哪里??
  • 它被称为 JavaScript 模板字面量。去阅读MDN:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 我知道我的意思是它应该是 index 而不是 id
  • 是的,我刚刚注意到您的评论后没有定义 id 哈哈
  • @entio 我已经尝试过你的密钥前缀,但我得到了一个错误。我为此设置了赏金,因为我真的很难弄清楚这一点。
【解决方案2】:

正如 cmets 中指出的,同一元素上不可能有多个 v-for 语句,您需要这样的东西:

<div class="course-list-row">
        <th style="width:5%"> Start date </th>   
        <th style="width:5%"> Event name</th>
        <th scope="col">Market</th>
        <th scope="col">Runner</th>

        <tr v-for="(event, index) in events" :key=id>
            <td>{{ event.start_time }} </td>
            <td>{{ event.event_name }} </td>
            <td></td>
            <td></td>
        </tr>

        <tr v-for="(market, index) in markets" :key=id>
            <td></td>
            <td></td>
            <td>{{ market.name }} </td>
            <td></td>
        </tr>

        <tr v-for="(runner, index) in runners" :key=id>  
            <td></td>
            <td></td>
            <td></td>
            <td>{{ runner.name }} </td>
        </tr>
 </div>

为避免使用上述冗长的模板,您可以将所有三个数组合并为一个数组,其中每个对象都有一个 id 和一个类型并呈现如下:

    <tr v-for="(entry, index) in combined" :key=id>
        <td>{{entry.type == 'event' ? event.start_time : ''}} </td>
        <td>{{ entry.type == 'event' ?  event.event_name : ''}} </td>
        <td>{{ entry.type = 'market' ? market.name : ''}}</td>
        <td>{{ entry.type == 'runner' ? runner.name : ''}}</td>
    </tr>

【讨论】:

  • 我已经尝试了您的两种解决方案。第一个渲染事件和市场,但 Runners 不渲染。市场列表在事件数据结束时开始。第二种解决方案对我不起作用。将后端中的数据更改为嵌套会更容易吗?我不知道为什么用 Vue 很难做到这一点。
  • 为什么 Runners 不渲染?有控制台错误吗?而且您不需要更改后端的数据,但在后端创建新路由可能更容易,例如get('/api/combined/') 结合了三种数据类型。
【解决方案3】:

虽然我认为 cmets 已经涵盖了这一点,但我将快速回顾一下原始代码的一些问题。

一个 HTML 元素不能多次具有相同的属性。虽然 Vue 模板在技术上不是 HTML,但它们确实会尝试遵循大致相同的解析规则,因此会丢弃重复项。原始代码在&lt;tr&gt; 上具有重复的v-forkey 属性。

另一个问题是key 值。编写:key=id 将尝试将key 设置为等于名为id 的属性的值。但是这个属性是在哪里定义的?默认情况下,Vue 将在 Vue 实例本身上查找它。我怀疑你的意思是让它使用eventmarketrunnerid 属性。为此,您需要写 :key="event.id" 等。

这个问题对于你想要达到的目标确实有些模糊,但下面的代码是我对你想要的最好的猜测:

new Vue({
  template: `
    <table border>
      <thead>
        <th>Start date</th>   
        <th>Event name</th>
        <th>Market</th>
        <th>Runner</th>
      </thead>
      <tbody>
        <template v-for="event in events">
          <tr :key="'event' + event.id">
              <td>{{ event.start_date }}</td>
              <td>{{ event.event_name }}</td>          
          </tr>
          <template v-for="market in marketsFor[event.id]">
            <tr :key="'market' + market.id">
              <td></td>
              <td></td>
              <td>{{ market.market_name }} </td>
            </tr>
            <tr v-for="runner in runnersFor[market.id]" :key="'runner' + runner.id">
              <td></td>
              <td></td>
              <td></td>
              <td>{{ runner.runner_name }}</td>
            </tr>
          </template>
        </template>
      </tbody>
    </table>
  `,
  
  el: '#app',
  
  data () {
    return {
      events: [
        {
          "id": 103,
          "event_name": "Guilherme Clezar vs Uladzimir Ignatik",
          "start_date": "12-11-19"
        }, {
          "id": 104,
          "event_name": "Event 2",
          "start_date": "13-11-19"
        }        
      ],
      markets: [
        {
          "id": 151,
          "market_name": "Moneyline",
          "event": 103
        }, {
          "id": 152,
          "market_name": "Market 2",
          "event": 103
        }, {
          "id": 153,
          "market_name": "Market 3",
          "event": 104
        }
        
      ],
      runners: [
        {
          "id": 1,
          "runner_name": "Runner 1",
          "market": 151
        }, {
          "id": 2,
          "runner_name": "Runner 2",
          "market": 151
        }, {
          "id": 3,
          "runner_name": "Runner 3",
          "market": 152
        }, {
          "id": 4,
          "runner_name": "Runner 4",
          "market": 153
        }
      ]
    }
  },
  
  computed: {
    marketsFor () {
      const markets = {}
      
      for (const market of this.markets) {
        const event = market.event
        markets[event] = markets[event] || []
        markets[event].push(market)
      }
      
      return markets
    },
    
    runnersFor () {
      const runners = {}
      
      for (const runner of this.runners) {
        const market = runner.market
        runners[market] = runners[market] || []
        runners[market].push(runner)
      }
      
      return runners
    }
  }
})
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<div id="app"></div>

我不得不重命名一些属性,例如start_timestart_datemarket.namemarket.market_name。模板中的属性似乎与示例数据不匹配。

我的解决方案的核心是一对计算属性,它们生成简单的对象映射,可用于在每个级别找到相应的子对象。对此有多种替代方案,但似乎任何解决方案的核心都是找到一种从模板内导航层次结构的方法。我的示例中的模板可以简化,但代价是将复杂性转移到组件的 JavaScript 部分。

【讨论】:

    猜你喜欢
    • 2021-05-15
    • 2018-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 2012-08-28
    相关资源
    最近更新 更多