Giwrgos Gkogkas
Giwrgos Gkogkas

Reputation: 479

AngularJS - Json to Tree structure

I 've seen lots of answers that turn a Json response to a Ul Tree structure. The thing is that all these subjects where around a nested response pointing out the relationship between parent object and child object.

I have a non nested Json response indicating the relation by reference to the objects property.

The following is part of the response:

{"id":"1","parent_id":null,"name":"Item-0"}, {"id":"2","parent_id":"1","name":"Item-1"}, {"id":"3","parent_id":"2","name":"Item-2"}, {"id":"4","parent_id":"2","name":"Item-4"}, {"id":"5","parent_id":"2","name":"Item-5"}, {"id":"6","parent_id":"2","name":"Item-6"}, {"id":"7","parent_id":"2","name":"Item-7"}, {"id":"8","parent_id":"2","name":"Item-8"}, {"id":"9","parent_id":"2","name":"Item-9"}, {"id":"10","parent_id":"1","name":"Item-3"}, {"id":"11","parent_id":"10","name":"Item-10"},

You might already noticed that the each object conects with his father through the parent_id which is conected to his parent's id.

I tried to create a custom directive to read the response and through recursion to build the Tree structure.

Till now I succeeded to only create the first level of the tree. Demo

app.directive('tree',function(){
    var treeStructure = {
        restrict: "E",
        transclude: true,
        root:{
            response : "=src",
            Parent : "=parent"
        },
        link: function(scope, element, attrs){
            var log = [];
            scope.recursion = "";
            angular.forEach(scope.response, function(value, key) {
                if(value.parent_id == scope.Parent){
                    this.push(value);
                }
            }, log);
            scope.filteredItems = log;

            scope.getLength = function (id){
                var test = [];
                angular.forEach(scope.response, function(value, key) {
                    if(value.parent_id == id){
                        this.push(value);
                    }
                }, test);
                if(test.length > 0){
                    scope.recursion = '<tree src="scope.response" parent="'+id+'"></tree>';
                }
                return scope.recursion;
            };
        },
        template:
            '<ul>'
                +'<li ng-repeat="item in filteredItems">'
                    +'{{item.name}}<br />'
                    +'{{getLength(item.id)}}'
                +'</li>'
            +'<ul>'
    };
    return treeStructure;
});

app.controller('jManajer', function($scope){
    $scope.information = {
        legend : "Angular controlled JSon response",
    };

    $scope.response = [
        {"id":"1","parent_id":null,"name":"Item-0"},
        {"id":"2","parent_id":"1","name":"Item-1"},
        {"id":"3","parent_id":"2","name":"Item-3"},
        {"id":"4","parent_id":"2","name":"Item-4"},
        {"id":"5","parent_id":"2","name":"Item-5"},
        {"id":"6","parent_id":"2","name":"Item-6"},
        {"id":"7","parent_id":"2","name":"Item-7"},
        {"id":"8","parent_id":"2","name":"Item-8"},
        {"id":"9","parent_id":"2","name":"Item-9"},
        {"id":"10","parent_id":"1","name":"Item-2"},
        {"id":"11","parent_id":"10","name":"Item-10"},
    ];
});

Is there any one who could show me how to convert this kind of array to Tree structure through recursion?

Upvotes: 2

Views: 5777

Answers (2)

vorillaz
vorillaz

Reputation: 6266

You need to unflatten your data before passing it to your view. I have modified your code a bit, also I have transformed your data and replaced the depth and relational fields with integers, it's more easy to compare integers rather than comparing strings and null values.

In this example I have used Undescore.js' powerful functions in order to unflatten the array.

The code can be found as a helper function inside your directive:

unflatten = function (array, parent, tree) {

    tree = typeof tree !== 'undefined' ? tree : [];
    parent = typeof parent !== 'undefined' ? parent : {
        id: "0"
    };

    var children = _.filter(array, function (child) {
        return child.parent_id == parent.id;
    });

    if (!_.isEmpty(children)) {
        if (parent.id == "0" || parent.id == null) {
            tree = children;
        } else {
            parent['children'] = children
        }
        _.each(children, function (child) {
            unflatten(array, child)
        });
    }
    console.log(tree)
    return tree;
}

If you want a VanillaJs solution check out this piece of code

Also here is a demo simulating an interactive tree with your data

The underscore unflatten function was taken from here .

Upvotes: 1

micha
micha

Reputation: 1238

Transform your array first recursive in a nested one

var myApp = angular.module('myApp', []);

myApp.controller('jManajer', function($scope) {
  $scope.res = [];
  $scope.response = [{
    "id": "1",
    "parent_id": 0,
    "name": "Item-0"
  }, {
    "id": "2",
    "parent_id": "1",
    "name": "Item-1"
  }, {
    "id": "3",
    "parent_id": "2",
    "name": "Item-3"
  }, {
    "id": "4",
    "parent_id": "2",
    "name": "Item-4"
  }, {
    "id": "5",
    "parent_id": "2",
    "name": "Item-5"
  }, {
    "id": "6",
    "parent_id": "2",
    "name": "Item-6"
  }, {
    "id": "7",
    "parent_id": "2",
    "name": "Item-7"
  }, {
    "id": "8",
    "parent_id": "2",
    "name": "Item-8"
  }, {
    "id": "9",
    "parent_id": "2",
    "name": "Item-9"
  }, {
    "id": "10",
    "parent_id": "1",
    "name": "Item-2"
  }, {
    "id": "11",
    "parent_id": "10",
    "name": "Item-10"
  }, ];

  function getNestedChildren(arr, parent) {
    var out = []
    for (var i in arr) {
      if (arr[i].parent_id == parent) {
        var children = getNestedChildren(arr, arr[i].id)

        if (children.length) {
          arr[i].children = children
        }
        out.push(arr[i])
      }
    }
    return out
  }
  $scope.res = getNestedChildren($scope.response, "0");

  console.log($scope.res);
  $scope.nested_array_stingified = JSON.stringify($scope.res);

});
<!DOCTYPE html>
<html>

<head>
  <script src="https://code.angularjs.org/1.3.11/angular.js"></script>

</head>

<body ng-app="myApp" ng-controller="jManajer">

  {{nested_array_stingified}}
</body>

</html>

After that you can follow the tutorial here for example http://sporto.github.io/blog/2013/06/24/nested-recursive-directives-in-angular/

Upvotes: 3

Related Questions