Multiple <td> elements per v-for loop

9.3k views Asked by At

In my Vue app, I am looping through an array of schools. Each school has a name, an array of teacher counts (one per grade), and an array of student counts (one per grade).

The following code works, but only because I am coding the <td>s manually.

new Vue({
  el: '#app',
  data: {
    schools: [
      { name: 'Lincoln',   teachers: [3, 4, 1], students: [55, 42, 39] },
      { name: 'Oak Grove', teachers: [1, 2, 1], students: [31, 36, 23] },
      { name: 'Fairview',  teachers: [1, 3, 2], students: [30, 26, 39] },
    ],
  },
});
thead th,
tbody td { text-align: center; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>

<table id="app" class="table">
  <thead>
    <tr>
      <th rowspan="2"></th>
      <th colspan="2">Grade K</th>
      <th colspan="2">Grade 1</th>
      <th colspan="2">Grade 2</th>
    </tr>
    <tr>
      <th>Teachers</th>
      <th>Students</th>
      <th>Teachers</th>
      <th>Students</th>
      <th>Teachers</th>
      <th>Students</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="school in schools">
      <th>{{ school.name }}</th>
      
      <td>{{ school.teachers[0] }}</td>
      <td>{{ school.students[0] }}</td>
      
      <td>{{ school.teachers[1] }}</td>
      <td>{{ school.students[1] }}</td>
      
      <td>{{ school.teachers[2] }}</td>
      <td>{{ school.students[2] }}</td>
    </tr>
  </tbody>
</table>

Notice repeated lines:

<td>{{ school.teachers[x] }}</td>
<td>{{ school.students[x] }}</td>

It is not too much of a problem on this simplified example. But in my real project, there are many more columns and sub-columns. Is there a way to do a repeating loop to display the <td>s?

I've tried another v-for loop, but since it's inside the <tr>, only <td> and <th> are allowed.

1

There are 1 answers

1
Bert On BEST ANSWER

Assuming the teachers and students arrays are always going to be the same length, you can iterate over a template tag.

<tr v-for="school in schools">
  <th>{{ school.name }}</th>
  <template v-for="(cnt, idx) in school.teachers.length">
    <td>{{ school.teachers[idx] }}</td>
    <td>{{ school.students[idx] }}</td>
  </template>
</tr>

Example.

new Vue({
  el: '#app',
  data: {
    schools: [
      { name: 'Lincoln',   teachers: [2, 2, 2], students: [40, 40, 40] },
      { name: 'Oak Grove', teachers: [2, 2, 2], students: [40, 40, 40] },
      { name: 'Fairview',  teachers: [2, 2, 2], students: [40, 40, 40] },
    ],
  },
});
thead th,
tbody td { text-align: center; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>

<table id="app" class="table">
  <thead>
    <tr>
      <th rowspan="2"></th>
      <th colspan="2">Grade K</th>
      <th colspan="2">Grade 1</th>
      <th colspan="2">Grade 2</th>
    </tr>
    <tr>
      <th>Teachers</th>
      <th>Students</th>
      <th>Teachers</th>
      <th>Students</th>
      <th>Teachers</th>
      <th>Students</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="school in schools">
      <th>{{ school.name }}</th>
      <template v-for="(cnt, idx) in school.teachers.length">
        <td>{{ school.teachers[idx] }}</td>
        <td>{{ school.students[idx] }}</td>
      </template>
    </tr>
  </tbody>
</table>

Another way to do this is to abstract the repeated elements into a component and use the special is attribute (<td is="details" v-for="..."></td>).