Vuejs2 - Iterate “tr” element recursively

Viewed 1833

I'm pretty new to Vue and I love it so far. However, I've encountered the following problem and I'll be thankful for any suggestions on how to get around it.

I would like to iterate recursively on an objects to get a nested table.

I have this template:

<script type="text/x-template">
    <tr>
        <td><a v-on:click="toggle">[+]</a></td>
        <td>...</td>
    </tr>
    <tr is="row-item"  v-show="open" ...></tr>
</script>

It's recursive, so each line tr has an other hidden line tr below.

When the user click on the [+] on the parent line, the children lines will appear.

I already try to wrap the content within a tbody tag, but then I get tbody inside tbody, which is still an illegal table layout and breaks it.

<script type="text/x-template">
    <tbody>
        <tr>
            <td>...</td>
            <td>...</td>
        </tr>
        <tr is="row-item" ...></tr>
    </tbody>
</script>

Fiddle: https://jsfiddle.net/rafi16d/puwcs9ay/

Vue@1.x doesn't require exactly one root element. How can I do without ?

Has anyone run into anything similar?

Thanks.

3 Answers

If you flatten the tree (normalize it) in a computed property, then you can render the HTML table without recursion within the component itself, and thus avoid hacking or generating invalid HTML.

Vue.component('tree-table-row', {
  data() {
    return {
      indent: 20,
      left_padding: 5
    };
  },
  props: ['node'],
  template: '#tree-table-row'
}); 
      
var app = new Vue({
  el: '#tree-as-table',
  data: {
    tree: {
      id: 0,
      value: "fruits",
      name: "root",
      children: [
      {
        id: 1,
        name: "A",
        children: [{
          id: 2,
          name: "B",
          value: "banana" 
        }],
        value: "apple"
      },
      {
        id: 3,
        name: "C",
        children: [{
          id: 4,
          name: "D",
          value: "durian" 
        }],
        value: "cherry"
      }
      ]
    }
  },
  computed: {
    tree_array() {
      return this.normalizedTree(this.tree, 0);
    }
  },
  methods: {
    normalizedTree(node, level) {
      var _this = this;
      var normal_element = {id: node.id, name: node.name, value: node.value, level: level};
      var array_fragment = [normal_element];
      if (node.children && node.children.length > 0) {
        var i;
        for (i=0; i < node.children.length; i++) {
          array_fragment = array_fragment.concat(_this.normalizedTree(node.children[i], level+1));
        };
      }
      return array_fragment;
    }
  }
});
.vue-table {
  border-collapse: collapse;
  font-family: sans-serif;
} 

td {
  border: 1px solid lightgrey;
  padding: 2px 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<script type="text/x-template" id="tree-table-row">
  <tr>
    <td :style="{'padding-left': node.level * indent + left_padding + 'px'}">{{node.name}}</td>
    <td>{{node.value}}</td>
  </tr>
</script>
    
<div id="tree-as-table">
  <table class="vue-table">
    <tr is="tree-table-row" v-for="(node, index) in tree_array" :node="node" :key="node.id"/>
  </table>
</div>

Related