1

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 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much, the solution works great!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.