EcStud
EcStud

Reputation: 61

Vue.js root element on table rows

Using this hierarchy of components in vue.js app:

<Root>
  <Table>
    <Row>
    <Row>
    ...
    <Row>

The poblem is with root element in Row component. At first it was table TR element. But now I don't know how many TR rows will be in Row component. Template tag can't be root element.

How it's better to organize Row controller with many TR rows?

<script type="text/template" id="row-template">
<tr>
  <td>{{row.name}}</td>
  <td>{{row.price}}</td>
</tr>
<tr>
  <td>-</td>
  <td>{{row.params[0].name}}</td>
</td>
<tr>
  <td>-</td>
  <td>{{row.params[1].name}}</td>
</td>
</script>

Upvotes: 3

Views: 2319

Answers (3)

Roy J
Roy J

Reputation: 43881

In addition to the multiple-tbody trick, it will be necessary to use the is attribute with a tbody tag to instantiate the component so that you don't have a component tag where it is not legal.

new Vue({
  el: "#foo",
  components: {
    componentName: {
      props: ['first'],
      template: '#cn-template'
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<template id="cn-template">
    <tbody>
      <tr>
        <td>{{first}}</td>
      </tr>
      <tr>
        <td>two</td>
      </tr>
    </tbody>
</template>
<table id="foo">
  <tbody>
    <tr>
      <td>Parent</td>
    </tr>
  </tbody>
  <tbody is="componentName" first="one">
  </tbody>
</table>

Upvotes: 3

blobmaster
blobmaster

Reputation: 853

Multiple <tbody/> is a very good answer but sometimes it's not enough.

For a similar question I purpose an other answer : Vue js error: Component template should contain exactly one root element

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

Libby
Libby

Reputation: 1022

It's actually valid html to have multiple <tbody> tags in a table so you could have that be your root element for each component.

<script type="text/template" id="row-template">
<tbody>
  <tr>
    <td>{{row.name}}</td>
    <td>{{row.price}}</td>
  </tr>
  <tr>
    <td>-</td>
    <td>{{row.params[0].name}}</td>
  </td>
  <tr>
    <td>-</td>
    <td>{{row.params[1].name}}</td>
  </tr>
</tbody>
</script>

Upvotes: 2

Related Questions