Adam Zerner
Adam Zerner

Reputation: 19168

Vue: How to display each "leaf" of an array of nested objects in a table?

I have an array that looks like this:

var arr = [{
  text: '1'
}, {
  text: '2'
  children: [{
    text: '2.1'
  }]
}, {
  text: '3',
  children: [{
    text: '3.1'
  }, {
    text: '3.2',
    children: [{
      text: '3.2.1'
    }, {
      text: '3.2.2'
    }]
  }]
}];

I want to display only the "leafs" of this array in a table, while showing the parents of the leafs. I want to generate the following HTML:

<table>
  <tr>
    <td>1</td>
  </tr>
  <tr>
    <td>2 | 2.1</td>
  </tr>
  <tr>
    <td>3 | 3.1</td>
  </tr>
  <tr>
    <td>3 | 3.2 | 3.2.1</td>
  </tr>
  <tr>
    <td>3 | 3.2 | 3.2.2</td>
  </tr>
</table>

I tried the following, but it doesn't work because the <section>s are interspersed with the <tr>s.

<section v-for="parent in arr">
  <tr v-if="!parent.children">
    <td>{{ parent.text }}</td>
  </tr>
  <section v-if="parent.children" v-for="child in parent.children">
    <tr v-if="!child.children">
      <td>{{ parent.text + ' | ' + child.text }}</td>
    </tr>
    <tr v-if="child.children" v-for="grandchild in child.children">
      <td>{{ parent.text + ' | ' + child.text + ' | ' + grandchild.text }}</td>
    </tr>
  </section>
</section>

Upvotes: 1

Views: 322

Answers (1)

thanksd
thanksd

Reputation: 55634

In Vue, it's always better to try and simplify your data structure to cater to your display needs than to try and find a roundabout way of displaying your data using directives in the template.

Usually, the best way to simplify your data is via computed properties. As @RoyJ suggested, you can make a computed property which would recursively generate a flattened array from your object's tree structure, and then reference that computed in the template instead.

Here's how I would do it:

new Vue({
  el: '#app',
  data() {
    return {
      arr: [{
        text: '1'
      }, {
        text: '2',
        children: [{
          text: '2.1'
        }]
      }, {
        text: '3',
        children: [{
          text: '3.1'
        }, {
          text: '3.2',
          children: [{
            text: '3.2.1'
          }, {
            text: '3.2.2'
          }]
        }]
      }]
    }
  },
  computed: {
    flatArr() {
      let flatArr = [];
      let addToFlatArr = ({ items, base }) => {
      	items.forEach((item) => {
          let { text, children } = item;
          let val = (base || '')  + text;
          if (children) {
            addToFlatArr({ items: children, base: val + ' | '})
          } else {
            flatArr.push(val);
          }
        })
      };
      addToFlatArr({ items: this.arr });
      return flatArr;
    }   
  },
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>

<div id='app'>
  <table>
    <tr v-for="item in flatArr">
      <td>{{ item }}</td>
    </tr>
  </table>  
</div>

Upvotes: 2

Related Questions