Search code examples
javascripthtmlvue.jshtml-table

Nested custom components inside table rendered in the wrong place?


I want to render a table, and as I would need to work on the cell, I define them through a component.

Below is the example in the fiddle:

Vue.component('table-cell', {
    props: {
        'r': 0,
        'c': 0
    }, 
    template: '<td style="border: 1px solid black; width:2em;">{{ c }}</td>'
});

var app = new Vue({
    el: '#app',
    data: {
        rows: 3,
        cols: 3,
    },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
    <table>
        <tbody>
            <tr v-for="r in rows">
                <table-cell v-for="c in cols" v-bind:key="c">
            </tr>
        </tbody>
    </table>
</div>

But the generated code is not what I expected :

<div id="app">
    <td></td>
    <td></td>
    <td></td>
    <table>
        <tbody>
            <tr></tr>
            <tr></tr>
            <tr></tr>
        </tbody>
    </table>
</div>

For a reason I do not understand, my td elements are generated outside the tr ones, and everything seems to work as if my two loops were not nested. When I change the <table-cell> with simple <td> (not using a component), it works as expected.


Solution

  • You ran into an HTML parsing edge case which is described in the Vue docs under HTML Template Parsing Caveats. The workaround is to use the is attribute on custom components inside of tables. Here's a fixed working example of your code:

    Vue.component('table-cell', {
        props: {
            r: Number,
            c: Number,
        }, 
        template: '<td>{{r}}, {{c}}</td>',
    })
    
    var app = new Vue({
        el: '#app',
        data: {
            rows: 3,
            cols: 3,
        },
    })
    td {
      border: 1px solid black;
      padding: 5px 10px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    
    <div id="app">
      <table>
        <tbody>
          <tr v-for="r in rows" :key="r">
            <td is="table-cell" v-for="c in cols" :key="c" :r="r" :c="c">
            </td>
          </tr>
        </tbody>     
      </table>
    </div>