2

I have a Vue (v2.5.16) component which uses a <tbody> with a repeating <tr> element in the template. The component data is empty initially, and when I add data, Vue is placing the <tbody> and table rows above and outside the table, just after the #clines div. Why?

Here is the html in question. The component is srvaudit-table-rows, under the existing <tbody> tag. The component template uses an additional <tbody> as its parent element. Multiple <tbody> elemenets are allowed inside a <table> according to this answer.

<div id="app" class="container-fluid">
    <div class="row">
        <div class="col-xs-12">
            <div id="clines" class="table-responsive ctable">
                <table class="table table-sm nowrapp">
                    <thead>
                        <tr>
                            <th>Command</th>
                            <th>Directory</th>
                            <th>Return</th>
                            <th>Pipes</th>
                            <th>When</th>
                            <th>Duration</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>time date</td>
                            <td>/root</td>
                            <td>42</td>
                            <td>2</td>
                            <td>Feb 2</td>
                            <td>44s</td>
                        </tr>
                    </tbody>
                    <srvaudit-command-list></srvaudit-command-list>  // Desired placement of component template and data.
                </table>
            </div>
        </div>
    </div>
</div>

Here is the component.

Vue.component('srvaudit-command-list', {
    template: `
        <tbody>
            <tr v-for="(command, index) in commands" :key="index">
                <td>{{ command.cmd }}</td>
                <td>{{ command.cwd }}</td>
                <td>{{ command.rval }}</td>
                <td>{{ command.pipes }}</td>
                <td>{{ command.begin }}</td>
                <td>{{ command.duration }}</td>
            </tr>
        </tbody>
    `,
    props: ['command'],
    data() {
        return {
            commands: []
        };
    },
    mounted: function() {
        Echo.private(`session.${sid}.commands`)
            .listen('CommandReceived', (data) => {
                this.commands.push(data.command);  // Here is where it renders the component after pushing some data.
            });
    },
});

This is how it renders the html after pushing the new command to the commands data.

<div id="app" class="container-fluid">
    <div class="row">
        <div class="col-xs-12">
            <div id="clines" class="table-responsive ctable">
                <tbody>                       // incorrect placement
                    <tr>                      // incorrect placement
                        <td>time date</td>    // incorrect placement
                        <td>/root</td>        // incorrect placement
                        <td>42</td>           // incorrect placement
                        <td>2</td>            // incorrect placement
                        <td>Feb 2</td>        // incorrect placement
                        <td>44s</td>          // incorrect placement
                    </tr>                     // incorrect placement
                </tbody>                      // incorrect placement
                <table class="table table-sm nowrapp">
                    <thead>
                        <tr>
                            <th>Command</th>
                            <th>Directory</th>
                            <th>Return</th>
                            <th>Pipes</th>
                            <th>When</th>
                            <th>Duration</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>time date</td>
                            <td>/root</td>
                            <td>42</td>
                            <td>2</td>
                            <td>Feb 2</td>
                            <td>44s</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>

Any help is much appreciated! I've been struggling with this for a couple days now and tried it every which way I can find on SO and in the Vue docs.

Niklesh Raut
  • 34,013
  • 16
  • 75
  • 109
Cotton
  • 99
  • 2
  • 12

1 Answers1

1

Interpolation or component replacement can be done only when if vue will get any tag, As there can't be any other tag except table,tbody,thead,tr,td. You can cross check by just put any tag inside table tag, without using vue, it will remove atomatically from table tag and put outside of table tag and without table tag it tbody does not display anything.

You can check other discussion regarding this and one more

Here is the solution to make insert tbody inside table tag by vue.js, with another component base-table

Vue.component('base-table',{
 template:`<div>
   <table class="table table-sm nowrapp">
                    <thead>
                        <tr>
                            <th>Command</th>
                            <th>Directory</th>
                            <th>Return</th>
                            <th>Pipes</th>
                            <th>When</th>
                            <th>Duration</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>time date</td>
                            <td>/root</td>
                            <td>42</td>
                            <td>2</td>
                            <td>Feb 2</td>
                            <td>44s</td>
                        </tr>
                    </tbody>
                    <srvaudit></srvaudit>
                </table>
                </div>
  `
});

Vue.component('srvaudit', {
    template: `
          <tbody>
                        <tr>
                            <td>time date</td>
                            <td>/root2</td>
                            <td>99</td>
                            <td>8</td>
                            <td>Feb 5</td>
                            <td>55s</td>
                        </tr>
                        <tr>
                            <td>time date</td>
                            <td>/root4</td>
                            <td>101</td>
                            <td>10</td>
                            <td>Feb 25</td>
                            <td>88s</td>
                        </tr>
                    </tbody>
    `,
    props: ['command'],
    data() {
        return {
            commands: []
        };
    },
    mounted: function() {
    },
});
new Vue({
  el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script></script>

<div id="app" class="container-fluid">
    <div class="row">
        <div class="col-xs-12">
            <div id="clines" class="table-responsive ctable">
                <base-table></base-table>
            </div>
        </div>
    </div>
</div>

JSFiddle for the same

Niklesh Raut
  • 34,013
  • 16
  • 75
  • 109
  • My base table has existing data, though... how would I work around this issue with Vue not rendering the base table? ...edit: figuring it out, I'll extract the base table into Vue instead of inline rendering. Thanks! – Cotton Apr 18 '18 at 00:17