adamfhelm
adamfhelm

Reputation: 35

How do I use a vue.js nested for loop over two arrays with a v-for

This was resolved and my parent view and child component code are now correct and functioning Using Vue.js, I'm attempting to iterate analogous to a nested for loop to render a matrix table display. To achieve this I'm attempting to use a nested v-for loop but couldn't get it to work originally.

The data I'm using to build the table exists as a schedule of dates, each coming from a schedule collection in MongoDB. For each given day all the teams play a match against another team. The match data for each team lives in a schedule array in each document.

To pare down the approach and accomplish this I've created a schedule table of all the teams' schedules in the parent view, and passed it off as a property to the child component to streamline the v-for coding.

The traditional pattern for what I'm attempting to do in the child component is logically:

for(const i=0; i<schedules.length; i++) {
for(const j=0; j<weeks.length; j++) {
  console.log("This match is " + schedule[i] + " on " + week[j]) 

On the child component I've tried the following two strategies to no avail:

1.) Wrapping a v-for in a div:

<div v-for="(schedule,j) in schedules" :key="j">
        <sui-table-cell v-for="(number, i) in weeks" :key="i">
        {{schedules[schedule][i].team}}
        </sui-table-cell>
</div>      

2.) Wrapping a v-for in a template (this code doesn't compile):

<template v-for="schedule in schedules">
        <sui-table-cell v-for="(number, i) in weeks" :key="i">
        {{schedules[schedule][i].team}}
        </sui-table-cell>
</template>    

The data should look like so (if you click on the image you can see where I cannot get this to iterate):

Correct View Of Data

Parent view template.

<template>
  <div>
    <h1 class="ui header center aligned">ACL</h1>
    <h4 class="ui header center aligned schedule">
      <em>CRIBBAGE SCHEDULE</em>
    </h4>
    <sui-table id="standings" class="celled table center aligned">
      <inner-table :weeks="totalWeeks" :allPlayers="allPlayers" />
    </sui-table>
  </div>
</template>
<script>
import TableForm from "./TableForm.vue";
import { api } from "../../../helpers/helpers.js";
export default {
  name: "schedule-header",
  components: { "inner-table": TableForm },
  data() {
    return {
      totalWeeks: [],
      allDates: [],
      allPlayers: []
    };
  },
  async mounted() {
    this.totalWeeks = await api.getTotalWeeks();
    this.allDates = await api.getAllDates();
    this.allPlayers = await api.getTeams();
  }
};
</script>
<style scoped>
h4.schedule {
  color: brown;
}
</style>

Child Component:

<template>
  <div>
    <sui-table-header>
      <sui-table-row>
        <sui-table-header-cell></sui-table-header-cell>
        <sui-table-header-cell>TEAMS</sui-table-header-cell>
        <sui-table-header-cell v-for="(number, i) in weeks" :key="i">WK{{i+1}}</sui-table-header-cell>
      </sui-table-row>
      <sui-table-row>
        <sui-table-cell></sui-table-cell>
        <sui-table-cell></sui-table-cell>
        <sui-table-cell v-for="(number, i) in weeks" :key="i">{{ number.gameDate.substring(0,10) }}</sui-table-cell>
      </sui-table-row>
    </sui-table-header>
    <sui-table-row v-for="(player, i) in allPlayers" :key="i">
      <sui-table-cell>{{i+1}}</sui-table-cell>
      <sui-table-cell>{{ player.player1 }}&{{player.player2}}</sui-table-cell>
      <sui-table-cell v-for="(week, j) in weeks" :key="j">{{player.schedule[j]}}</sui-table-cell>
    </sui-table-row>
  </div>
</template>

<script>
export default {
  props: ["weeks", "allPlayers"]
};
</script>

Upvotes: 0

Views: 1632

Answers (1)

Alex Brohshtut
Alex Brohshtut

Reputation: 2060

Vue uses key property to differ between components and prevent unnecessary rendering. So from your code :key="i", Vue understands that all sui-table-cell with key i are the same. That's why you've got repeats. You should provide unique keys to avoid this.

This code would do, however I would not recommend to use index as a key and provide some better keying strategy.

<sui-table-row v-for="(player, i) in allPlayers" :key="i">
      <sui-table-cell>{{i+1}}</sui-table-cell>
      <sui-table-cell>{{ player.player1 }}&{{player.player2}}</sui-table-cell>
      <div v-for="(schedule,j) in schedules" :key="j">
        <sui-table-cell v-for="(number, i) in weeks" :key="`${j}_${i}`">
          {{schedules[j][i]}}
        </sui-table-cell>
      </div>
</sui-table-row>

Sample data:

{
    schedules: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],[2,3,4,5,6,7,8,9,1]]
    weeks: [1/1,1/7,1/14,1/21,1/28,2/5,2/12,2/19,2/25,3/3]
}

The solution with template, will not really work, because template part is never rendered, it is used when you want to use v-for, for multiple internal elements such as:

<template v-for="...">
 <h1></h1>
 <p></p>
</template>

Upvotes: 1

Related Questions