NgSelect - options for embedding with arbitrary depth

In my application, I have an array of objects with the following structure

[{"ID":1, "parentID":0, "name":"Parent #1"}, {"ID":2, "parentID":0, "name":"Parent #2"}, {"ID":3, "parentID":1, "name":"Child #1 1"}, {"ID":4, "parentID":3, "name":"child #1 2"}, {"ID":5, "parentID":2, "name":"child #2 1"}, {"ID":6, "parentID":5, "name":"child #2 2"}] 

I would like to present this as a selection menu that would allow the user to select a node sheet while the output non-selectable parent nodes displayed the hierarchy of the structure.

I tried several approaches, the most successful of which was in my angular template, something like the following.

 <div ng-repeat="(idx, category) in $scope.allCats"> <select ng-model="$scope.cats[idx]" ng-options="cat as cat.name group by $scope.parentName(cat.parentID, idx) for cat in $scope.allCategories track by cat.ID"> <option value="">Select A Category</option> </select> </div> 

$scope.allCats is an array, and the $scope.parentName() method returns a string.

Problems with this are shown in the following screenshot. Namely, all parent elements appear twice, once as <option> and once as <optgroup> , while I would prefer that they appear only as a selectable element, but with them, obviously, the parent element, and the hierarchy of the structure supported; child nodes with ancestors and descendants do not appear in the correct tree-tree structure.

screenshot

How can I change the input or my angular pattern to achieve the desired behavior?

That is, to display the entire hierarchy, as defined by the attributes of the parentID , therefore each family has a common ancestor and the parent elements appear only once.

I suspect this is complicated by the fact that more than one level of descendants is possible, and because I would like to keep it as general as possible.

+4
source share
2 answers

How about sorting an array into areas before passing it to an angular template ?. If you have:

 var arr = [ {"ID":1, "parentID":0, "name":"Parent #1"}, {"ID":3, "parentID":1, "name":"Child #1 1"}, {"ID":4, "parentID":3, "name":"child #1 2"}, {"ID":7, "parentID":4, "name":"asdfadsf"}, {"ID":2, "parentID":0, "name":"Parent #2"}, {"ID":5, "parentID":2, "name":"child #2 1"}, {"ID":6, "parentID":5, "name":"child #2 2"} ]; 

It would be easier. Just show it as it is.

Code for sorting:

 var sortKeys = {}; // computes a unique sort key function getSortKey(item) { if(sortKeys[item.ID] === undefined) { var parentItem = arr.filter(function(it) { return it.ID === item.parentID; })[0]; if(parentItem != null) { return getSortKey(parentItem) + '' + item.ID; } return item.parentID + '' + item.ID; } return sortKeys[item.ID]; } // build sort keys to be used to sort items following their hierarchy arr.forEach(function(item) { sortKeys[item.ID] = getSortKey(item); }); // sort the array arr.sort(function(item1, item2) { var item1SortKey = sortKeys[item1.ID], item2SortKey = sortKeys[item2.ID]; return item1SortKey < item2SortKey ? -1 : (item1SortKey > item2SortKey ? 1: 0); }); 

 var arr = [{"ID":1, "parentID":0, "name":"Parent #1"}, {"ID":2, "parentID":0, "name":"Parent #2"}, {"ID":3, "parentID":1, "name":"Child #1 1"}, {"ID":4, "parentID":3, "name":"child #1 2"}, {"ID":5, "parentID":2, "name":"child #2 1"}, {"ID":6, "parentID":5, "name":"child #2 2"}, {"ID":7, "parentID":4, "name":"asdfadsf"}]; var sortKeys = {}; // computes a unique sort key function getSortKey(item) { if(sortKeys[item.ID] === undefined) { var parentItem = arr.filter(function(it) { return it.ID === item.parentID; })[0]; if(parentItem != null) { return getSortKey(parentItem) + '' + item.ID; } return item.parentID + '' + item.ID; } return sortKeys[item.ID]; } // build sort keys to be used to sort items following their hierarchy arr.forEach(function(item) { sortKeys[item.ID] = getSortKey(item); }); // sort the array arr.sort(function(item1, item2) { var item1SortKey = sortKeys[item1.ID], item2SortKey = sortKeys[item2.ID]; return item1SortKey < item2SortKey ? -1 : (item1SortKey > item2SortKey ? 1: 0); }); document.getElementById('result').innerHTML = JSON.stringify(arr, null, 2); 
 <pre id="result"></pre> 
+2
source

You can do this using the filter function.

In this example, I simplified your data by changing the ParentID to the actual parent name. (You use the function to replace, but the concept is the same)

Data:

 $scope.data = [{ "ID": 1, "parentID":"Parent #1", name: "Parent #1" }, { "ID": 2, "parentID": "Parent #1", name: "Parent #2" }, { "ID": 3, "parentID": "Parent #2", name: "Child #1 1" }, { "ID": 4, "parentID": "Parent #3", name: "child #1 2" }, { "ID": 5, "parentID": "Parent #3", name: "child #2 1" }, { "ID": 6, "parentID": "Parent #6", name: "child #2 2" }] 

View:

 <select ng-model="data" ng-options="d as d.name group by d.parentID for d in data | filter:checkparent(d)"></select> 

Please note that we have added a function call that will be used to filter the list. This is the function inside the controller:

 $scope.checkparent = function () { return function (d) { return d.name != d.parentID; } } 

And this will be the result:

enter image description here

In your specific case, you can call your function, which returns the name of the parent inside the filter function, for example:

(not 100% sure what your function does, but hope the concept is clear)

 $scope.checkparent = function () { return function (d) { return d.name != $scope.parentName(d.parentID, idx); } } 
+2
source

All Articles