2

In my vue app, I have an array of objects which I want to render as a list. Within each object, there is a 'parent' property which contains info about who the parent is thereby giving us a hierarchical data structure. However, I am unable to show this hierarichal list. I tried creatin a tree by transforming the data but my code seems bugged.

You can see my attempted version here: http://jsbin.com/pewusiyete/edit?html,js,console,output

Code

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>

  <div id="app">
    <ul>
      <li v-for="item in list">
        <a v-bind:href="item.value">{{item.label}}</a>
      </li>
    </ul>
    {{tree}}
  </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            list:[
   {
      "label":"Parks & Gardens",
      "type":"place",
      "value":"parks-gardens",
      "id":"0CNYF4qh0aaYi2XLGCfG"
   },
   {
      "label":" Art & Craft",
      "type":"product",
      "value":"art-craft",
      "id":"4TfXwraLAJX9K5WeIBT5"
   },
   {
      "label":"Monuments",
      "type":"place",
      "value":"monuments",
      "id":"4mVxy4QEplxTnf6NwpIf"
   },
   {
      "label":"Books",
      "type":"book",
      "value":"books",
      "id":"4oVqbEDhPSYqaTDn4xMV"
   },
   {
      "label":"Sports Academies",
      "type":"place",
      "value":"sports-academies",
      "id":"H7GkAF0Hfdu3OoHBYyQY"
   },
   {
      "label":"Store",
      "type":"place",
      "value":"store",
      "id":"Ki4YjRNe4HmZWOCOpg9K"
   },
   {
      "label":"Tennis Academies",
      "parent":"sports-academies",
      "type":"place",
      "value":"tennis-academies",
      "id":"N89adEZwHfSGMQiq1R5f"
   },
   {
      "label":"Toy Stores",
      "type":"place",
      "parent":"stores",
      "value":"toy-stores",
      "id":"Oj6QgO0S0Z6AFHvrr2ZH"
   },
   {
      "label":"Electronics",
      "type":"product",
      "value":"electronics",
      "id":"UuztFKZrsw3vcciKaj8k"
   },
   {
      "label":"Tech",
      "type":"product",
      "value":"tech",
      "id":"WuqiVSXxmlCCQ5usAUNZ"
   },
   {
      "label":"Book Stores",
      "type":"place",
      "parent":"stores",
      "value":"book-stores",
      "id":"ZmXlJ12jJROGeHjYcwOT"
   },
   {
      "label":" Online Stores",
      "type":"commerce",
      "value":"online-stores",
      "id":"cIRSVPcqSDr6WfuRe4OX"
   },
   {
      "label":"Play Areas",
      "type":"place",
      "value":"play-areas",
      "id":"fEk3dcKprq9Hd8rSgiG3"
   },
   {
      "label":"Toys",
      "type":"product",
      "value":"toys",
      "id":"rJTpw2V9apxe9jQjLTOS"
   },
   {
      "label":"Stores",
      "type":"place",
      "value":"stores",
      "id":"ZmXlJ12jJROGeHjYcwOH"
   }
]
        },
        computed: {
          newitem: function () {
            return this.list.reduce(function(p,c) {
              p[c.value] = c;
              c.children = [];
              return p;
              }, {}); 
          },
          tree: function () {
            return this.list.reduce(function(p,c) {
              console.log(c.parent)
            if (c.parent = 'undefined') {
              p = c;
            } else {
              newitem[c.parent].children.push(c);
            }
            return p;
            }, {});
          }
        }     
    });
</script>
</body>
</html>

1 Answer 1

1

There's a few things wrong with your code, I won't go into too much detail, but at a high level:

  • if (c.parent = 'undefined') should probably be if (c.parent === undefined).
  • newitem[c.parent].children.push(c) should probably be this.newitem[c.parent].children.push(c), but even then you shouldn't mutate computed properties.
  • I think just overall you are not structuring your menu items properly.

First you will need to transform the flat list of items into a tree structure. Then you will need to use recursive components in order to render such a tree.

Here's an example:

const MenuList = {
  name: 'menu-list',
  template: '#menu-list-template',
  props: ['items'],
};

new Vue({
  el: '#app',
  components: {
    MenuList,
  },
  data: {
    list: [
      {
        "label": "Parks & Gardens",
        "type": "place",
        "value": "parks-gardens",
        "id": "0CNYF4qh0aaYi2XLGCfG"
      },
      {
        "label": " Art & Craft",
        "type": "product",
        "value": "art-craft",
        "id": "4TfXwraLAJX9K5WeIBT5"
      },
      {
        "label": "Monuments",
        "type": "place",
        "value": "monuments",
        "id": "4mVxy4QEplxTnf6NwpIf"
      },
      {
        "label": "Books",
        "type": "book",
        "value": "books",
        "id": "4oVqbEDhPSYqaTDn4xMV"
      },
      {
        "label": "Sports Academies",
        "type": "place",
        "value": "sports-academies",
        "id": "H7GkAF0Hfdu3OoHBYyQY"
      },
      {
        "label": "Store",
        "type": "place",
        "value": "store",
        "id": "Ki4YjRNe4HmZWOCOpg9K"
      },
      {
        "label": "Tennis Academies",
        "parent": "sports-academies",
        "type": "place",
        "value": "tennis-academies",
        "id": "N89adEZwHfSGMQiq1R5f"
      },
      {
        "label": "Toy Stores",
        "type": "place",
        "parent": "stores",
        "value": "toy-stores",
        "id": "Oj6QgO0S0Z6AFHvrr2ZH"
      },
      {
        "label": "Electronics",
        "type": "product",
        "value": "electronics",
        "id": "UuztFKZrsw3vcciKaj8k"
      },
      {
        "label": "Tech",
        "type": "product",
        "value": "tech",
        "id": "WuqiVSXxmlCCQ5usAUNZ"
      },
      {
        "label": "Book Stores",
        "type": "place",
        "parent": "stores",
        "value": "book-stores",
        "id": "ZmXlJ12jJROGeHjYcwOT"
      },
      {
        "label": " Online Stores",
        "type": "commerce",
        "value": "online-stores",
        "id": "cIRSVPcqSDr6WfuRe4OX"
      },
      {
        "label": "Play Areas",
        "type": "place",
        "value": "play-areas",
        "id": "fEk3dcKprq9Hd8rSgiG3"
      },
      {
        "label": "Toys",
        "type": "product",
        "value": "toys",
        "id": "rJTpw2V9apxe9jQjLTOS"
      },
      {
        "label": "Stores",
        "type": "place",
        "value": "stores",
        "id": "ZmXlJ12jJROGeHjYcwOH"
      }
    ]
  },
  computed: {
    treeList() {
      // Deep clone the list and add a children property with empty array value to each item
      const items = this.list.map(item => Object.assign({}, item, { children: [] }));
      
      // Organize items into a map keyed by item value for easy lookup
      const byValue = new Map(items.map(item => [item.value, item]));
      
      // Top level will contain the items which do not have a parent
      const topLevel = [];
      for (const item of items) {
        // Look up the parent item if there is one
        const parent = byValue.get(item.parent);

        if (parent) {
          // Append the item into the parent's children array
          parent.children.push(item);
        } else {
          // The item has no parent
          topLevel.push(item);
        }
      }
      
      return topLevel;
    }
  }
});
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>

<div id="app">
  <menu-list :items="treeList"></menu-list>
</div>

<script type="text/x-template" id="menu-list-template">
  <ul v-if="items.length">
    <li v-for="item of items">
      <a :href="item.value">{{ item.label }}</a>
      <menu-list :items="item.children"></menu-list>
    </li>
  </ul>
</script>

Sign up to request clarification or add additional context in comments.

3 Comments

Not that Map is not supported in older browsers (read: IE) and rwquires a polyfill in those.
Thanks so much. This works perfectly. I took some time to understand my mistakes and the code you've provided. But it makes perfect sense!
I have two more confusions here. 1) When the nested list is created, is it possible to assign two different classes to the parent <ul> and to the child <ul> How do I do that? 2) Is it possible to sort the parent list alphabetically?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.