0

This is a challenging task for an AngularJS newbie like me so I would like to ask some help or assistance. So what I want to achieve is to group the data via tree level and tree id.

My current code for the HTML:

<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>

<body>

  <div ng-app="myApp" ng-controller="myCtrl">
      <ul>
          <li ng-repeat="item in list">
              [[ item.code ]]

          </li>
      </ul>
  </div>

</body>

</html>

My current code for the JS:

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

app.controller('myCtrl', function($scope) {

    $scope.list = [
        {
            "id": 1,
            "code": "TANK",
            "tree_id": 1,
            "level": 0,
            "parent": null
        },
        {
            "id": 2,
            "code": "OIL",
            "tree_id": 1,
            "level": 1,
            "parent": 1
        },
        {
            "id": 3,
            "code": "CHEM",
            "tree_id": 1,
            "level": 1,
            "parent": 1
        },
        {
            "id": 6,
            "code": "PROD",
            "tree_id": 1,
            "level": 2,
            "parent": 3
        },
        {
            "id": 4,
            "code": "BULK",
            "tree_id": 2,
            "level": 0,
            "parent": null
        },
        {
            "id": 5,
            "code": "LOG",
            "tree_id": 2,
            "level": 1,
            "parent": 4
        }
    ],
});

If you checked the $scope.list, you can find the tree_id, level and parent. So what I really want to achieve is to group the objects via tree id then the level 0 or top level will be the one present while the level 1 and up will be just be a collapsible content. Please take that the tree_id serves as a groupings between the object, the level can be considered as their hierarchy order and heir hierarchy root will depend on the integer value of the parent which is a tree id. Also please be aware that this is not limited to one branch but multiple, flexible and unlimited branches

The render should look something like this:

  • TANK
    • CHEM
    • OIL
      • PROD
  • BULK
    • LOG
Dean Christian Armada
  • 6,724
  • 9
  • 67
  • 116

4 Answers4

1

It's already discussed in StackOverFlow. You can use following link.

How can I group data with an Angular filter?

Using groupBy filter you can achieve tree structure.

Community
  • 1
  • 1
  • Hi! I appreciate the answer but I kinda changed the question into a more interesting and challenging one. The answers in your link is just limited to one level – Dean Christian Armada Mar 29 '16 at 12:06
1

You can as well use custom directives to handle recursive levels :

EDIT to add properties réf in directives and your new array structure

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

app.directive('collection', function () {
 return {
  restrict : "E",
  replace : true,
        transclude: true,
  scope : {
   collection : '=',
   list : '=',
   coldisplay : '=',
   colfrom : '=',
   colto : '='
  },
  template : "<ul><member ng-repeat='member in collection track by $index' member='member' list='list' coldisplay='coldisplay' colfrom='colfrom' colto='colto'></member></ul>"
 };
});

app.directive('member', function ($compile) {
 return {
  restrict : "E",
  replace : true,
        transclude: true,
  scope : {
   member : '=',
   list : '=',
   coldisplay : '=',
   colfrom : '=',
   colto : '='
  },
  template : "<li>{{member[coldisplay]}}</li>",
  link : function (scope, element, attrs) {
   scope.children = [];
   angular.forEach(scope.list, function (value, key) {
    if (value[scope.colfrom] === scope.member[scope.colto]) {
     this.push(value);
    }
   }, scope.children);

   if (scope.children.length > 0) {
    element.append("<collection collection='children' list='list' coldisplay='coldisplay' colfrom='colfrom' colto='colto'></collection>");
    $compile(element.contents())(scope);
   }
  }
 }
});

app.controller('myCtrl', function ($scope) {

 $scope.list = [{
   "id" : 1,
   "code" : "TANK",
   "tree_id" : 1,
   "level" : 0,
   "parent" : null
  }, {
   "id" : 2,
   "code" : "OIL",
   "tree_id" : 1,
   "level" : 1,
   "parent" : 1
  }, {
   "id" : 3,
   "code" : "CHEM",
   "tree_id" : 1,
   "level" : 1,
   "parent" : 1
  }, {
   "id" : 6,
   "code" : "PROD",
   "tree_id" : 1,
   "level" : 2,
   "parent" : 3
  }, {
   "id" : 4,
   "code" : "BULK",
   "tree_id" : 2,
   "level" : 0,
   "parent" : null
  }
 ];

 $scope.rootList = [];

 angular.forEach($scope.list, function (value, key) {
  if (value.parent == null) {
   this.push(value);
  }
 }, $scope.rootList);

})
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>

<body>
  <div ng-app="myApp" ng-controller="myCtrl">
   <collection collection='rootList' list='list' coldisplay='"code"' colfrom='"parent"' colto='"id"'></collection>
  </div>
</body>
</html>

Inspired by https://toddmotto.com/everything-about-custom-filters-in-angular-js/

Rogerio Soares
  • 1,613
  • 16
  • 14
0

in angular groupBy work's for object not for array. so you can generate this in your controller as you need.

I can show you a process to generate as you want. If want can try it.

in your controller call a function after getting you list from api.

    $scope.newList = {parents:[],childs:[]};


    var generateNav = function(parent) {
      $scope.newList.parents.push(parent);
      angular.forEach($scope.list, function(item) {
        if(item.tree_id === parent.tree_id) {
          $scope.newList.childs.push(item);
        }
      })
    };

    var generateNewList = function(list) {
      angular.forEach(list, function(item) {
      if(item.level === 0) {
        generateNav(item);
      }
      });
    };

    // after api cal and get data call this function with your `list`
    generateNewList($scope.list);

and In html: used newList

<ul>
          <li ng-repeat="item in newList.parents">
              {{ item.code }}
              <ul>
                <li ng-repeat="child in newList.childs" ng-if="child.tree_id === item.tree_id && item.id!==child.id">{{child.code}}</li>
              </ul>
          </li>
      </ul>

you can visit PLUNKER DEMO LINK

Shaishab Roy
  • 16,335
  • 7
  • 50
  • 68
0

to fix your issue I would suggest few changes. First of all since you are getting data from an API we need to fix the format to get a more reasonable structure. Something like below

[{
  id: 1,
  code: "TANK",
  tree_id: 1,
  parent: null,
  children: [{
    id: 3,
    code: "CHEM",
    tree_id: 1,
    parent: 1,
    children: [{
        id: 9,
        code: "PROD",
        tree_id: 1,
        parent: 3
    }]
  }, {
    id: 2,
    code: "OIL",
    tree_id: 1,
    parent: 1,
  }]
}, {
  id: 4,
  code: "BULK",
  tree_id: 2,
  parent: null,
  children: [{
    id: 5,
    code: "LOG",
    tree_id: 2,
    parent: 4,
  }]
}]

For this we need to write function as below

var createObject = function(json) {
    return {
        id: json.id,
        code: json.code,
        tree_id: json.tree_id,
        parent: json.parent,
        children: []
    }
};

var processList = function(temporaryObj) {

    for (var i = 0; i < list.length; i++) {
        if (list[i].parent === temporaryObj.id) {
            temporaryObj.children.push(processList(createObject(list[i])));
        }
    }

    return temporaryObj;
};

var convertList = function(list) {

    var temp = [];
    for (var i = 0; i < list.length; i++) {

        if (list[i].level === 0) {
            temp.push(processList(createObject(list[i])));
        }

    }

    return temp;
};

var newList = convertList(list_from_api);

And after this we need to create a recursive directive to print content in UI

app.directive("tree", ['$compile', '$templateCache', function($compile, $templateCache) {
  return {
    type: "E",
    scope: {
      list: "="
    },
    link: function(scope, element, attrs) {
      element.replaceWith( $compile( $templateCache.get('tree.html'))(scope));
    }
  }
}]);

where tree.html is as below

<ul ng-if="list.length > 0">
   <li ng-repeat="item in list">
      <span>{{ item.code }}</span>
      <tree list="item.children"></tree>
   </li>
</ul>

You can refer to this plunkr link for more details https://plnkr.co/edit/8TtgFfgUQd73Du47b7XB?p=preview

S4beR
  • 1,872
  • 1
  • 17
  • 33