steschwa
steschwa

Reputation: 105

Output nested array properly

I try to build a data tree in Vue, using components.

Consider the following data:

"data": [
{
  "id": 1,
  "name": "foo",
  "children": [
    {
      "id": 2,
      "name": "bar",
      "children": []
    },
    {
      "id": 3,
      "name": "hulu",
      "children": []
    }
  ]
},
{
  "id": 4,
  "name": "foobar",
  "children": [
    {
      "id": 5,
      "name": "foobar hulu",
      "children": []
    }
  ]
}]

Now i want to output it with a table like:

ID ║ Name ║ Path
1 ║ foo ║ /foo
2 ║ bar ║ /foo/bar
3 ║ hulu ║ /foo/hulu
4 ║ foobar ║ /foobar
5 ║ foobar hulu ║ /foobar/foobar hulu

I've tried using a component that "calls" itself if there are children available. The problem is that Vue.js only allows one root element.

My components:

var Element = {
    props: ['context', 'path'],
    name: 'self',
    template: `
        <tr>
            <td>{{context.id}}</td>
            <td>{{context.name}}</td>
            <td>{{path}}</td>
        </tr>
        <self v-if="context.children.length != 0" v-for="child in context.children" :context="child" :path="path + '/' + child.name"></self>
    `
};

var Tree = {
    components: {
        'element': Element
    },
    template: `
        <table v-if="elements.length != 0">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Path</th>
                </tr>
            </thead>

            <element v-for="element in elements" :context="element" :path="'/' + element.name"></element>
        </table>
    `,

How would you bypass this problem? I've tried wrapping the element template inside a tbody. This will calculate the path correctly and outputs all elements, however that would produce rows that are nested inside columns and looks really ugly.

enter image description here

Any ideas?

Upvotes: 1

Views: 613

Answers (1)

Bert
Bert

Reputation: 82439

Flatten the paths.

Vue.component("flat-tree",{
  props:["paths"],
  template: "#flat-tree-template",
  methods:{
    flatten(data, root, accumulator){
      return data.reduce((acc, val) => {
        accumulator.push({
          id: val.id,
          name: val.name,
          path: root + val.name
        });
        if (val.children)
          return this.flatten(val.children, root + val.name + "/", accumulator);
        else
          return accumulator;
      }, accumulator);
    }
  },
  computed:{
    flattened(){
      return this.flatten(this.paths, "/", []);
    }
  }
})

Template

<template id="flat-tree-template">
  <table>
    <tr v-for="path in flattened">
      <td>{{path.id}}</td>
      <td>{{path.name}}</td>
      <td>{{path.path}}</td>
    </tr>
  </table>
</template>

Working example.

Upvotes: 1

Related Questions