how to use recursive function to select the parent in a tree array using angulat ui tree?

539 Views Asked by At

So, I'm trying to create a hierarchical tree. Whena node is selected that has children, then all the children of the node is selected, but when I select all the children I also want to select the parent.

here is a link to the plunker: [https://plnkr.co/plunk/iMBFfy6cf7urOHhZ][1]

I have created a directory to markup the tree

TreeController.js

 (function (ng) {
    var app = ng.module('tree', ['tree.service', 'tree.directives']);
    app.controller("TreeController", ["TreeService", "$scope", function (TreeService, $scope) {
        var tc = this;
        buildTree();
        function buildTree() {
            TreeService.getTree().then(function (result) {
                tc.tree = result.data;
            }, function (result) {
                alert("Tree no available, Error: " + result);
            });
        }
        
     
       $scope.selectedItems = [];
       
       $scope.getSelected = function(){
         $scope.selectedItems = [];
         function checkChildren(c) {
              angular.forEach(c.children, function (c) {
                 if (c.checked){
                    $scope.selectedItems.push({"selected":c.name});
                 }
                  checkChildren(c);
              });
         }
         
         
          angular.forEach(tc.tree, function(value, key) {
              if (value.checked){
                $scope.selectedItems.push({"selected":value.name});
              }
              
               checkChildren(value);
          });
       };
    }]);
})(angular);

index.html

 <div ng-controller="TreeController as tc">
    <ul class="tree">
        <node-tree children="tc.tree"></node-tree>
    </ul>
    
    <button ng-click="getSelected()">Get Selected</button>
    
    <br/>
    <br/>
    Selected: 
    <ul>
          <li ng-repeat="item in selectedItems">
            {{item.selected}}
          </li>
        </ul>
    </div>

TreeDirective.js

 (function (ng) {
        var app = ng.module('tree.directives', []);
        app.directive('nodeTree', function () {
            return {
                template: '<node ng-repeat="node in tree"></node>',
                replace: true,
                restrict: 'E',
                scope: {
                    tree: '=children'
                }
            };
        });
        app.directive('node', function ($compile) {
            return {
                restrict: 'E',
                replace: true,
                templateUrl: 'node.html', // HTML for a single node.
                link: function (scope, element) {
                    /*
                     * Here we are checking that if current node has children then compiling/rendering children.
                     * */
                    if (scope.node && scope.node.children && scope.node.children.length > 0) {
                        scope.node.childrenVisibility = true;
                        var childNode = $compile('<ul class="tree" ng-if="!node.childrenVisibility"><node-tree children="node.children"></node-tree></ul>')(scope);
                        element.append(childNode);
                    } else {
                        scope.node.childrenVisibility = false;
                    }
                },
                controller: ["$scope", function ($scope) {
                   
                    // This function is for just toggle the visibility of children
                    $scope.toggleVisibility = function (node) {
                        if (node.children) {
                            node.childrenVisibility = !node.childrenVisibility;
                        }
                    };
                    // Here We are marking check/un-check all the nodes.
                    $scope.checkNode = function (node) {
                        node.checked = !node.checked;
                        // if (node.checked){
                        //   alert("clicked");
                        // }
                        function checkChildren(c) {
                            angular.forEach(c.children, function (c) {
                                c.checked = node.checked;
                                checkChildren(c);
                            });
                        }
    
                        checkChildren(node);
                    };
                }]
            };
        });
    })(angular);
   

node.html

  <li>
    <span ng-click="toggleVisibility(node)"> {{ ( node.childrenVisibility && node.children.length ) ? '+' : '-' }}</span>
    <input ng-click="checkNode(node)" type="checkbox" ng-checked="node.checked">
    <span>
        {{ $index + 1 }}. {{ node.name }}
    </span>
</li>
1

There are 1 best solutions below

1
Jacob Stamm On BEST ANSWER

The first step is to determine what each node's parent node is. We can do that by recursing right after the tree is loaded and setting a parent property on each node.

TreeController.js

...
function buildTree() {
    TreeService.getTree().then(function (result) {
        tc.tree = result.data;
        
        function setParentForChildren(n) {
            angular.forEach(n.children, function (c) {
                c.parent = n;
                setParentForChildren(c);
            })
        }
        angular.forEach(tc.tree, setParentForChildren);
    }, function (result) {
        alert("Tree no available, Error: " + result);
    });
}
...

Now, we can use that parent reference each time a box is checked to recurse up the tree and say "if all my children are checked, then I should be checked too" for each parent node.

TreeDirective.js

...
$scope.checkNode = function (node) {
    node.checked = !node.checked;
    function checkParent(n) {
        if (!n.parent)
            return;
        const p = n.parent;
        p.checked = p.children.every(function(c) { return c.checked });
        checkParent(p);
    }
    
    checkParent(node);

    function checkChildren(c) {
        angular.forEach(c.children, function (c) {
            c.checked = node.checked;
            checkChildren(c);
        });
    }

    checkChildren(node);
};
...

Link to modified plunker