Search code examples
data-bindinghtml-tablevue.js

Populating HTML table rows with objects containing an array using Vue


I'm trying to populate rows in an HTML table using the Vue framework - the data is as seen below:

TeamRows: [
            { team: 'One', times: ['1', '2', '3'] },
            { team: 'Two', times: ['4', '5', '6'] },
            { team: 'Three', times: ['7', '8', '9'] }
          ]

I've tried following this codepen, but with bad result - this is my HTML:

<tbody>
   <tr v-for="(row, rindex) in teamRows">
      <td>{{rindex}}</td>
         <template>
            <cell v-for="(value, vindex) in row" :value="value" :vindex="vindex" :rindex="rindex"></cell>
         </template>
   </tr>
</tbody>
</table>
<template id="template-cell">
   <td>{{ value }}</td>
</template>

And this is the Vue component:

Vue.component('cell', {
    template: '#template-cell',
    name: 'row-value',
    props: ['value', 'vindex', 'rindex']
});

I would like the team to go in the first column in a row and the times to follow along in as many columns as there are times. Hope someone with more Vue knowledge is able to help me out here. Cheers.


Solution

  • Turns out the reason is you're using in-DOM template and browser moves unknown cell element above the v-for, and Vue can't access row value anymore: https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats

    A solution without cell component, just with inline cell elements, works fine. Also, template wrapper is not needed in a table template:

    new Vue({
      el: '#app',
      data: {
        teamRows: [
          { team: 'One', times: ['1', '2', '3'] },
          { team: 'Two', times: ['4', '5', '6'] },
          { team: 'Three', times: ['7', '8', '9'] }
        ]
      }
    });
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    
    <div id="app">
      <table>
        <tbody>
         <tr v-for="row in teamRows" :key="row.team">
           <td>{{ row.team }}</td>
           <td v-for="time in row.times">{{ time }}</td>
         </tr>
        </tbody>
      </table>
    </div>

    Also, as a result of a discussion, you can still use a component if you wrap your table into another component for example, so that browser don't interfere and Vue has a chance to render everything properly:

    new Vue({
      el: '#app',
      data: {
        teamRows: [
          { team: 'One', times: ['1', '2', '3'] },
          { team: 'Two', times: ['4', '5', '6'] },
          { team: 'Three', times: ['7', '8', '9'] }
        ]
      }, 
      components: {
        'mytable': {
          template: `<table>
        <tbody>
         <tr v-for="row in rows" :key="row.team">
           <td>{{ row.team }}</td>
           <cell v-for="time in row.times" :value="time" :key="time"></cell>
         </tr>
        </tbody>
      </table>`,
          props: ['rows'],
          components: {
            'cell': {
              template: `<td>{{ value }}</td>`,
              props: ['value'],
            }
          }
        }
      }
    });
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    
    <div id="app">
      <mytable :rows="teamRows"></mytable>
    </div>