vue.jsvue-component

Vue js simple component for table not working


I am beginner to vue js. I am trying learn step by step things from official vue documentation. I tried to understand component functionality and created following code:

 <div id="app">
     <table class="table">
         <tr>
            <td><strong>Name</strong></td>
            <td><strong>Email Address</strong></td>                        
         </tr>
         <contact-item v-for="item in contacts" v-bind:contact="item" v-bind:key="item.id"></contact-item>                    
     </table>
 </div>

and here is the javascript code for displaying row data from component template.

<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    Vue.component('contact-item', {
        props: ['contact'],
        template: '<tr><td>{{ contact.name }}</td><td>{{ contact.email }}</td></tr>'
    })
    var app = new Vue({
        el: '#app',
        data: {
            contacts: [
                {id: 1, name:'John Doe', email:'John@Doe.com'},
                {id: 2, name:'Peter Drunket', email:'Peter@Drunket.com'},
                {id: 3, name:'Mike Benjamin', email:'Mike@Benjamin.com'},
            ]
        }
    });        
</script>

The problem is data is displaying but not in table. It is displaying right after "app" div.

Output screenshot attached.enter image description here


Solution

  • There're certain caveats with parsing DOM templates if those mix Vue components and native elements.

    Some HTML elements, such as <ul>, <ol>, <table> and <select> have restrictions on what elements can appear inside them, and some elements such as <li>, <tr>, and <option> can only appear inside certain other elements.

    This will lead to issues when using components with elements that have such restrictions. For example:

    <table>
      <blog-post-row></blog-post-row>
    </table> 
    

    The custom component will be hoisted out as invalid content, causing errors in the eventual rendered output.

    In your case, it caused your table to be rendered 'above' the header. Actually, browser has created two tables in this case: one for <tr>s replacing the hoisted component, another for 'native' table that was there in the template from the beginning.

    Fortunately, the is special attribute offers a workaround. You need to specify the name of component that you're going to use to replace the specific native element. It's not quite convenient to specify the name of that element twice (first in HTML, then in component itself), but, like it's been said, it's a workaround.

    <table>
      <tr is="blog-post-row"></tr>
    </table>
    

    Here how it might look in your case:

    Vue.component('contact-item', {
      props: ['contact'],
      template: '<tr><td>{{ contact.name }}</td><td>{{ contact.email }}</td></tr>'
    })
    var app = new Vue({
      el: '#app',
      data: {
        contacts: [{
            id: 1,
            name: 'John Doe',
            email: 'John@Doe.com'
          },
          {
            id: 2,
            name: 'Peter Drunket',
            email: 'Peter@Drunket.com'
          },
          {
            id: 3,
            name: 'Mike Benjamin',
            email: 'Mike@Benjamin.com'
          },
        ]
      }
    });
    <div id="app">
      <table class="table">
        <tr>
          <td><strong>Name</strong></td>
          <td><strong>Email Address</strong></td>
        </tr>
        <tr is="contact-item" v-for="item in contacts" v-bind:contact="item" v-bind:key="item.id"></tr>
      </table>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>