Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

lonetraceur's avatar

JSON traversal without known keys in VUE.

Hey everyone. I'm a little stuck with what I assume is quite a simple problem and wondered if anyone could help.

I'm using an implementation of the Baum nested set to build a multi-level menu tree and I've sent that as JSON using the supplied toHeirarchy() method into a view composer which is provided to my view. The JSON structure is created a little like this:

{   "638":
        {   "id": "638"
            "title: "green"
            "children": {
                "0": {
                    id": "215"
                    "title: "fish"
                    "children": {...}
                }
                "1": {
                    id": "327"
                    "title: "chicken"
                    "children": {...}
                }
            }
        },
    "735":
        {   "id": "735"
            "title: "blue"
            "children": {...}
        }
}

Essentially, as you can see, the key name of the object at the top level is the ID of the record and the children's keys are sequential numbers of returned results.

So, in my VUE code, I've based my code off this jsfiddle: [https://jsfiddle.net/u4n1m04q/176/]

My HTML tag and template are:

<menutree :menutreedata="{{ $menuJSON }}"></menutree>

<template id="menutree-template">

    <ul class="menutop">
        <li>
            <div v-for="item in menutreedata">
                @{{ item.title }}
            </div>

            <ul>
                <menutree v-for="node in menutreedata" :menutreedata="node"></menutree>
            </ul>
        </li>
    </ul>
</template>

And my root VUE instance / Component is:

Vue.component ('menutree', {

    template: '#menutree-template',

    props: {
        menutreedata: Object
    },

});
new Vue ({
    el: 'body'
});

That's quite a bit of a long-winded question, but hopefully someone can understand it's actually a fairly simple problem... How do I loop over the JSON when the key is an unknown value? (ID). I've tried doing:

<menutree v-for="node in menutreedata.children" :menutreedata="node"></menutree>

But that doesn't hit the right properties because it actually needs to be something like:

<menutree v-for="node in menutreedata.[somthingHere].children" :menutreedata="node"></menutree>

Anyone have any ideas?

0 likes
8 replies
jimmck's avatar

You to ask the object/array for its keys.

/*
    Returns an array of Hashmap keys, or an emtpy array if there are none.
*/
function hashMap_keys()
{
    var key;
    var keys = new Array();
    var x = 0;
        
    for (key in this.hashMap)
    {
        keys[x] = key;
        ++ x;
    }
    
    return keys;
} // keys.

You can take the JSON input and make it a nice array to work with. JSON.parse(data);

lonetraceur's avatar

Very interesting. Thank you jimmck for the reply. Creating a helper function like this to traverse the object and return the keys is a good idea, I'm not sure how to implement it within VUE, but will give it a go and see if it's a viable solution. Cheers!

lonetraceur's avatar

That example was what I was actually basing the code upon. However, I had made a very small error which has now given me a breakthrough. The error was that I was declaring the property exclusively as an Object and actually the children were in an array... which was causing errors. Therefore, my code (which has been simplified a lot now!) is as follows:

The tag/template

<menutree :menutreedata="{{ $menuJSON }}"></menutree>

<template id="menutree-template">

    <ul class="menu">
        <li class="menutitle" v-for="item in menutreedata">
            @{{ item.title }}
                <menutree :menutreedata="item.children"></menutree>
        </li>
    </ul>

</template>

The VUE component and root.

Vue.component ('menutree', {

    template: '#menutree-template',

    props: {
        menutreedata: ''
    }

});
new Vue ({
    el: 'body'
});

The menutreedata property is now defaulted to an empty string rather than an object. So simple it's silly.

Thanks for your help though, it wouldn't have put me on the right track without the comment!

Cheers!

2 likes
Slothlike's avatar

I've been going in circles trying to solve this exact problem. Thank you so much for the solution! I was plodding along quite nicely with Vue, or so I thought until I got stuck on nested sets of data. I thought I could combine what I learned in the Laracasts (I'm up to episode 13 or so of the Vue series) with the tree example on the Vue website. Nope.

Your solution is so simple and great. It kills me that it wasn't one of the other thousand or so things I tried already.

How would you go about loading your menutree once with Ajax? like in this episode: https://laracasts.com/series/learning-vue-step-by-step/episodes/10

I tried loading it in my new Vue instance then have the component use that data, but I'm stuck again. I can't get it to work. This stuff is supposed to be so easy but I fear I'm missing some core concept along the way.

1 like
willvincent's avatar

I'm not sure how to implement it within VUE, but will give it a go and see if it's a viable solution.

It's just javascript, there's nothing magic you need to do to implement that function in Vue.. You could simply define it on the global window namespace so it's available everywhere. Or you could make it a method on a specific vue component. There's no wrong answer here since the function is definitely not vue specific.

2 likes
syaiful6's avatar

you can loop the like this.the key available on the loop as well as the value..

<div v-for="(key, val) in menutreedata">
</div>
1 like
lonetraceur's avatar

@willvincent. Good to know, thanks. I'm still very new to JS and VUE, so some seemingly obvious things are not quite obvious to me yet. So thanks for the heads up!

@syaiful6. Yes! I found this out a little while after reading @jimmck's comment. Definitely useful to know that I can relabel the key and value variable names.

@Slothlike. I feel your pain buddy! Trying to get nested sets working has been a real uphill challenge, but I'm nearly there. My data is being provided via a View Composer, not AJAX, but that may change in the future. However, I DO grab the details of a page that the menu is linked too via AJAX (so each menu item loads a new page). The way I've got my code is as follows (I've removed a couple of bits that aren't relevant):

Root VUE Instance - Listens for the dispatch message from the component and stores the data the message contains.

// Root VUE instance.
var rootVUE = new Vue ({
    el: 'body',

    data: {
        currentpage: ''
    },

    events: {
        'currentpagedetails': function(currentpagedetails){
            this.currentpage = currentpagedetails;
        }
    }
});

Vue Component - Grabs the data from the API URL by passing the menu ID. This then dispatches a message with the data up to the root VUE instance.

Vue.component ('menutree', {

    template: '#menutree-template',

    props: {
        menutreedata: ''
    },

    methods: {
        fetchPage: function($usermenuID) {

            // Define the endpoint for API resource.
            var pageresource = this.$resource('api/v1/usermenu/{id}/page');

            // Get current page resource with ID.
            var vm = this;
            pageresource.get({ id: $usermenuID }, function(retrievedpage) {

                this.$dispatch('currentpagedetails', retrievedpage);

            });
        }
    }
});

View HTML - Has an @click event handler that activates the AJAX method with the corresponding menu ID field passed to it.

<menutree :menutreedata="{{ $menuJSON }}"></menutree>

<template id="menutree-template">
    <ul class="menu">
        <li class="menuitem" v-for="treenode in menutreedata">
            <span @click="fetchPage(treenode.id)">@{{ treenode.title }}</span>
            <menutree :menutreedata="treenode.children"></menutree>
        </li>
    </ul>
</template>

API routes - These listen for AJAX requests with the menu ID and will send the relevant data back. This part is probably not correct REST format and I'll be looking back at this to redo.

Route::group(['prefix' => 'api/v1'], function(){

    Route::resource('usermenu','ApiUsermenuController');
    Route::get('usermenu/{id}/page','ApiUsermenuController@showpage');

});

ApiUsermenuController. The Route then runs @showpage, passing in the menu $id.

    public function showpage($id)
    {
        return Usermenu::find($id)->page()->get();
    }

Hopefully this helps a little and shows how AJAX retrieval works! Good luck!

2 likes

Please or to participate in this conversation.