2

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:'[email protected]'},
                {id: 2, name:'Peter Drunket', email:'[email protected]'},
                {id: 3, name:'Mike Benjamin', email:'[email protected]'},
            ]
        }
    });        
</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

2 Answers 2

4

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: '[email protected]'
      },
      {
        id: 2,
        name: 'Peter Drunket',
        email: '[email protected]'
      },
      {
        id: 3,
        name: 'Mike Benjamin',
        email: '[email protected]'
      },
    ]
  }
});
<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>

Sign up to request clarification or add additional context in comments.

2 Comments

Great! thats fixed the issue. I am beginner so dont know if its good practice to use "is='somename'" or using template <somename></somename> tag itself.
I'd say the best practice is to avoid using DOM templates: if you had a component for the whole table, you wouldn't have this issue. See, Vue parser doesn't read the HTML, trying to get the information from DOM instead. But browsers try their best to maintain the DOM the way it should be; hence the aforementioned hoisting and similar effects.
0

This workaround will help you too

Introduce custom table component which does nothing:

    Vue.component('my-table', {
    props: [''],
    emits: [],
    mounted: function () {
    },
    data: function () {
        return {
        }
    },
    methods: {
    },

    template:
        '<table>' +
        '<slot></slot>' +
        '</table>'
})

And just use this component instead of native 'table' tag

<div id="app">
     <my-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>                    
     </my-table>
 </div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.