mastrdevl
mastrdevl

Reputation: 13

Multi level nested menu from json

How do I create a nested menu (with "sub-sub-sub...-n") items from my JSON, based on the "id" and the "pid"?

Code details:

id - item ID
lvl - level of the item
pid - parent ID of the item
pos - position of the item
nm - name of the item

JSON:

[{"id":1,"lvl":0,"pid":0,"pos":0,"nm":"HomeTree"},
{"id":5,"lvl":1,"pid":1,"pos":0,"nm":"Books"},
{"id":2,"lvl":1,"pid":1,"pos":1,"nm":"Jobs"},
{"id":16,"lvl":1,"pid":1,"pos":2,"nm":"Music"},
{"id":17,"lvl":2,"pid":16,"pos":0,"nm":"Relax Sets"},
{"id":12,"lvl":1,"pid":1,"pos":3,"nm":"Cars"},
{"id":13,"lvl":2,"pid":12,"pos":0,"nm":"BMW"},
{"id":14,"lvl":1,"pid":1,"pos":4,"nm":"Web"},
{"id":15,"lvl":2,"pid":14,"pos":0,"nm":"PHP Analytics"},
{"id":18,"lvl":1,"pid":1,"pos":5,"nm":"Hardware"},
{"id":19,"lvl":2,"pid":18,"pos":0,"nm":"EVGA SR2 Upgrades"},
{"id":31,"lvl":2,"pid":18,"pos":1,"nm":"Keyboards"},
{"id":20,"lvl":1,"pid":1,"pos":6,"nm":"Movies"},
{"id":21,"lvl":1,"pid":1,"pos":7,"nm":"Games"},
{"id":22,"lvl":1,"pid":1,"pos":8,"nm":"Robots"},
{"id":25,"lvl":1,"pid":1,"pos":9,"nm":"Magazines"},
{"id":26,"lvl":2,"pid":25,"pos":0,"nm":"IT Security"},
{"id":27,"lvl":1,"pid":1,"pos":10,"nm":"Management"},
{"id":28,"lvl":2,"pid":27,"pos":0,"nm":"Holacracy"},
{"id":29,"lvl":3,"pid":28,"pos":0,"nm":"Testtt"}]

What I need:

HomeTree
    Books
    Jobs
    Music
    Relax Sets
    Cars
        BMW
    Web
        PHP Analytics
    Hardware
        EVGA SR2 Upgrades
    Keyboards
    Movies
    Games
    Robots
    Magazines
        IT Security
    Management
        Holacracy
            Testtt

My current code:

$.ajax({
    url: './jstree_head.php?operation=full_tree',
    data: "",
    dataType: 'json',
    success: function(data)
    {
        var output="<br />";
        for (var i in data)
        {
                var rid = data[i].id;
                var rpid = data[i].pid;
                var rlvl = data[i].lvl;
                var rnm = data[i].nm;

                var nest = '&nbsp; &nbsp; &nbsp;';
                if(rpid > 1) {
                                 nm = nest+rnm+'<br>';
                             }
                else {nm = rnm+'<br>';}

            output+='' + nm + '';
        }
        $('#content').html(output);
    }
});

Right now I can't account for deep sub-sub levels, what is the right way to do that?

--- UPDATE ---

What I tried is:

var nest = '&nbsp; &nbsp; &nbsp;';
if(rpid > 1) {
    nm = nest+rnm+'<br>';
}
else {nm = rnm+'<br>';}

output+='' + nm + '';

But this does only one level of parent items.

Upvotes: 0

Views: 2642

Answers (2)

Piyush
Piyush

Reputation: 826

Since your output is not formatted, I am first creating a hierarchy of parent-child which is then being used to print the output. Instead of directly storing the values in string, I am using JSON as it's much easier to dynamically add child there.

var data = [{"id":1,"lvl":0,"pid":0,"pos":0,"nm":"HomeTree"},
    {"id":5,"lvl":1,"pid":1,"pos":0,"nm":"Books"},
    {"id":2,"lvl":1,"pid":1,"pos":1,"nm":"Jobs"},
    {"id":16,"lvl":1,"pid":1,"pos":2,"nm":"Music"},
    {"id":17,"lvl":2,"pid":16,"pos":0,"nm":"Relax Sets"},
    {"id":12,"lvl":1,"pid":1,"pos":3,"nm":"Cars"},
    {"id":13,"lvl":2,"pid":12,"pos":0,"nm":"BMW"},
    {"id":14,"lvl":1,"pid":1,"pos":4,"nm":"Web"},
    {"id":15,"lvl":2,"pid":14,"pos":0,"nm":"PHP Analytics"},
    {"id":18,"lvl":1,"pid":1,"pos":5,"nm":"Hardware"},
    {"id":19,"lvl":2,"pid":18,"pos":0,"nm":"EVGA SR2 Upgrades"},
    {"id":31,"lvl":2,"pid":18,"pos":1,"nm":"Keyboards"},
    {"id":20,"lvl":1,"pid":1,"pos":6,"nm":"Movies"},
    {"id":21,"lvl":1,"pid":1,"pos":7,"nm":"Games"},
    {"id":22,"lvl":1,"pid":1,"pos":8,"nm":"Robots"},
    {"id":25,"lvl":1,"pid":1,"pos":9,"nm":"Magazines"},
    {"id":26,"lvl":2,"pid":25,"pos":0,"nm":"IT Security"},
    {"id":27,"lvl":1,"pid":1,"pos":10,"nm":"Management"},
    {"id":28,"lvl":2,"pid":27,"pos":0,"nm":"Holacracy"},
    {"id":29,"lvl":3,"pid":28,"pos":0,"nm":"Testtt"}];

var output="<br />";

var res = [];
function createRelationship(){
    for (var i in data)
    {
        if(data[i].pid == 0 && data[i].id == 1){
            res.push({
            id: data[i].id,
            nm: data[i].nm
            });
        }
        else{
            var parent = findParent(data[i].pid, res);
            if(parent){
                if(!parent.items){
                    parent.items = [];
                }
                parent.items.push({
                    id: data[i].id,
                    nm: data[i].nm
                });
            }
        }
    }
}

function findParent(pid, _res){
    if(_res){
        if(_res.id == pid){
            return _res;
        }
        var len = _res.length ? _res.length : _res.items ? _res.items.length : 0;
        for(var i =0; i< len; i++){
            if(_res[i] && _res[i].id == pid){
                return _res[i];
            }
            else{
                var child = _res[i] ? _res[i] : _res;
                if(child){
                    for(var j=0;j<child.items.length;j++){
                        var res = findParent(pid, child.items[j]);
                        if(res){
                            return res;
                        }
                    }
                }
            }
        }
    }
    return null;
}



function printOutPut(obj, spaceCount){
    var space = '';
    for(var i = 0; i< spaceCount; i++){
        space+= '&nbsp;';
    }
    output += space + obj.nm;
    output += "<br />";
    if(obj.items){
        for(i = 0; i < obj.items.length; i++){
            printOutPut(obj.items[i], (spaceCount + 4));
        }
    }
}

createRelationship();

for(var z =0; z<res.length;z++){
    printOutPut(res[z], 1);
}

document.write(output); 

Upvotes: 1

Soviut
Soviut

Reputation: 91555

Since your data is a flat array, as opposed to nested JSON objects, you can't use a recursive function.

Instead, you can loop over all the elements, keeping track of a list parents as the lvl attribute changes. As the lvl increases, add the previous element to your list of parents and use this latest parent for all following items. As the lvl decreases, remove a latest parent from the list and use the one before it in the list instead for all following items.

Upvotes: 0

Related Questions