Reputation: 3386
While I can render an HTML table fine using v-for and inline mustache syntax, I cannot achieve the same result using a component.
Vue / the browser removes the wrapping TABLE tag and inserts TRs outside a TABLE context, so they do not render properly:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
<h2>Vue table rows inline (works)</h2>
<table border="1">
<tr>
<th>Name</th>
<th>Value</th>
</tr>
<tr v-for="(row, index) in mydata">
<td>{{row.name}}</td>
<td>{{row.value}}</td>
</tr>
</table>
<h2>Vue table rows using component (broken)</h2>
<table border="1">
<tr>
<th>Name</th>
<th>Value</th>
</tr>
<my-row v-for="(row, index) in mydata" :key="row.name" :name="row.name" :val="row.value"></my-row>
</table>
</div>
<script>
Vue.component('my-row', {
props: ['name', 'val'],
template: '<tr><td>{{name}}</td><td>{{val}}</td></tr>'
})
new Vue({
el: "#app",
data: {
mydata: [{
name: "A",
value: 1
},
{
name: "B",
value: 2
},
{
name: "C",
value: 3
}
]
}
})
</script>
You can see this also at https://jsfiddle.net/MCAU/eywraw8t/128217/
What do I need to do to get the component version to work? (Adding a TBODY doesn't make any difference.)
Upvotes: 2
Views: 1970
Reputation: 3386
Oh I have now found https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats which explains that TRs are a special case and require the following syntax instead:
<tr is="my-row" v-for="(row, index) in mydata" :key="row.name" :name="row.name" :val="row.value"></tr>
Upvotes: 3
Reputation: 852
using functionnal component as suggested in : Vue js error: Component template should contain exactly one root element may do the trick.
copy/paste here :
if, for any reasons, you don't want to add a wrapper (in my first case it was for <tr/>
components), you can use a functionnal component.
Instead of having a single components/MyCompo.vue
you will have few files in a components/MyCompo
folder :
components/MyCompo/index.js
components/MyCompo/File.vue
components/MyCompo/Avatar.vue
With this structure, the way you call your component won't change.
components/MyCompo/index.js
file content :
import File from './File';
import Avatar from './Avatar';
const commonSort=(a,b)=>b-a;
export default {
functional: true,
name: 'MyCompo',
props: [ 'someProp', 'plopProp' ],
render(createElement, context) {
return [
createElement( File, { props: Object.assign({light: true, sort: commonSort},context.props) } ),
createElement( Avatar, { props: Object.assign({light: false, sort: commonSort},context.props) } )
];
}
};
And if you have some function or data used in both templates, passed them as properties and that's it !
I let you imagine building list of components and so much features with this pattern.
Upvotes: 0