Search code examples
angularjstoggleangularjs-ng-clickexpandangular-ng-if

Hide selection bar on parent when child is expanded using angularJS


I'm trying to create a sample application using angularJS out of JSON. I've created a tree like structure which can be expanded on click, and it shows the corresponding child nodes. Similarily, it hides the child nodes, on collapsing using click again.

Each time, I'm displaying a selection bar in blue, to indicate the expanded node. Whenever, a child node is expanded, the selection node on parent node should be hidden. It works fine, unless I've 2 child nodes for the same parent expanded.

For e.g., When Iexpand child nodes 'A' & 'B' under the node 'ABC', the selection bar on 'ABC' also appears, which is incorrect. Ideally, it should be only seen on child nodes - 'A & 'B'.

This, works fine when I've only '1' or '3' child nodes expanded. I guess there is something wrong with toggling 'obj.grandChild = !obj.grandChild; in the HTML.

Could you please help me fix this?

Here's the code:

angular.module('myApp', [])
 .controller('myCtrl', ['$scope', function($scope) {
  $scope.webs = [{
   'pageTitle': 'ABC',
   'pageUrl': 'http://www.example.net',
   'childPage': [{
    'subPageTitle': 'A',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'one',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'two',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'three',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }, {
    'subPageTitle': 'B',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'four',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'five',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'six',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }, {
    'subPageTitle': 'C',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'seven',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'eight',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'nine',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }]
  }, {
   'pageTitle': 'DEF',
   'pageUrl': 'http://www.example.net',
   'childPage': [{
    'subPageTitle': 'D',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'ten',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'eleven',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'twelve',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }, {
    'subPageTitle': 'E',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'thirteen',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'fourteen',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'fifteen',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }, {
    'subPageTitle': 'F',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'sixteen',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'seventeen',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'eighteen',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }]
  }, {
   'pageTitle': 'GHI',
   'pageUrl': 'http://www.example.net',
   'childPage': [{
    'subPageTitle': 'G',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'nineteen',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'twenty',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'twnety one',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }, {
    'subPageTitle': 'H',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'twentytwo',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'twentythree',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'twentyfour',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }, {
    'subPageTitle': 'I',
    'subPageUrl': 'http://www.example.net',
    'grandChild': [{
     'grandChildPageTitle': 'twentyfive',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'twentysix',
     'grandChildPageUrl': 'http://www.example.net'
    }, {
     'grandChildPageTitle': 'twentyseven',
     'grandChildPageUrl': 'http://www.example.net'
    }]
   }]
  }];
}]);
.main-link {
  font-weight: bold;
}

.showChild {
  position: relative;
  left: 0;
  float: left;
  margin-right: 10px;
  top: 3px;
  color: grey;
  font-size: 1.3em;
}

.showGrandChild {
  position: relative;
  left: 0;
  float: left;
  margin-right: 10px;
  top: 7px;
  color: red;
  font-size: 0.75em;
}

.parentBar {
  display: inline-block;
  height: 15px;
  width: 2px;
  background-color: blue;
  position: relative;
  left: -45px;
  top: 4px;
  float: left;
}

.childBar {
  display: inline-block;
  height: 15px;
  width: 2px;
  background-color: blue;
  position: relative;
  left: -18px;
  bottom: 156px;
  float: left;
}

ul {
  list-style: none;
}

li {
  margin: 20px;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
  <div ng-repeat="obj in webs"> 
	<ul ng-init="obj.showChild = false;" ng-click="obj.showChild = !obj.showChild;">
	  <li>
		<a ng-href="{{obj.pageUrl}}" class="main-link">{{obj.pageTitle}}</a>
		<i class="fa fa-arrow-down showChild" aria-hidden="true"></i>
		<span class="parentBar" ng-if="obj.showChild === true && obj.grandChild === false"></span>	
	  </li>				  
	</ul>
	<ul ng-init="child.showGrandChild = false; obj.grandChild = false;" ng-show="obj.showChild">
	  <li ng-repeat="child in obj.childPage" ng-click="child.showGrandChild = !child.showGrandChild; obj.grandChild = !obj.grandChild;">
		<a ng-href="{{child.subPageUrl}}">{{child.subPageTitle}}</a>
		<i class="fa fa-arrow-down showGrandChild" aria-hidden="true"></i>
		<ul ng-show="child.showGrandChild">
		  <li ng-repeat="grandChild in child.grandChild">
			<a ng-href="{{grandChild.grandChildPageUrl}}">{{grandChild.grandChildPageTitle}}</a>
		  </li>
		</ul>
		<span class="childBar" ng-if="child.showGrandChild === true"></span>
	  </li>						
	</ul>	
  </div>
</div>


Solution

  • I think this is what you wanted. You were correct, the confusion is in the obj.grandChild === false check. As you toggled the next grandChild, this was set back to false so the Parent's line would be displayed.

    I added a function called isChildrenShowing which will iterate and return true if any child is showing its grandChildren. MDN Documentation on some.

    $scope.isChildrenShowing = function(children) {
        return children.some(function(child) {
            return child.showGrandChild === true;
        });
    }
    

    Which means obj.grandChild is no longer required.

    angular.module('myApp', [])
     .controller('myCtrl', ['$scope', function($scope) {
      $scope.isChildrenShowing = function(children) {
        return children.some(function(child) {
          return child.showGrandChild === true;
        });
      }
     
      $scope.webs = [{
       'pageTitle': 'ABC',
       'pageUrl': 'http://www.example.net',
       'childPage': [{
        'subPageTitle': 'A',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'one',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'two',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'three',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }, {
        'subPageTitle': 'B',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'four',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'five',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'six',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }, {
        'subPageTitle': 'C',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'seven',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'eight',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'nine',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }]
      }, {
       'pageTitle': 'DEF',
       'pageUrl': 'http://www.example.net',
       'childPage': [{
        'subPageTitle': 'D',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'ten',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'eleven',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'twelve',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }, {
        'subPageTitle': 'E',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'thirteen',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'fourteen',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'fifteen',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }, {
        'subPageTitle': 'F',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'sixteen',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'seventeen',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'eighteen',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }]
      }, {
       'pageTitle': 'GHI',
       'pageUrl': 'http://www.example.net',
       'childPage': [{
        'subPageTitle': 'G',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'nineteen',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'twenty',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'twnety one',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }, {
        'subPageTitle': 'H',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'twentytwo',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'twentythree',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'twentyfour',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }, {
        'subPageTitle': 'I',
        'subPageUrl': 'http://www.example.net',
        'grandChild': [{
         'grandChildPageTitle': 'twentyfive',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'twentysix',
         'grandChildPageUrl': 'http://www.example.net'
        }, {
         'grandChildPageTitle': 'twentyseven',
         'grandChildPageUrl': 'http://www.example.net'
        }]
       }]
      }];
    }]);
    .main-link {
      font-weight: bold;
    }
    
    .showChild {
      position: relative;
      left: 0;
      float: left;
      margin-right: 10px;
      top: 3px;
      color: grey;
      font-size: 1.3em;
    }
    
    .showGrandChild {
      position: relative;
      left: 0;
      float: left;
      margin-right: 10px;
      top: 7px;
      color: red;
      font-size: 0.75em;
    }
    
    .parentBar {
      display: inline-block;
      height: 15px;
      width: 2px;
      background-color: blue;
      position: relative;
      left: -45px;
      top: 4px;
      float: left;
    }
    
    .childBar {
      display: inline-block;
      height: 15px;
      width: 2px;
      background-color: blue;
      position: relative;
      left: -18px;
      bottom: 156px;
      float: left;
    }
    
    ul {
      list-style: none;
    }
    
    li {
      margin: 20px;
    }
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
    <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
    <div ng-app="myApp" ng-controller="myCtrl">
      <div ng-repeat="obj in webs"> 
    	<ul ng-init="obj.showChild = false;" ng-click="obj.showChild = !obj.showChild;">
    	  <li>
    		<a ng-href="{{obj.pageUrl}}" class="main-link">{{obj.pageTitle}}</a>
    		<i class="fa fa-arrow-down showChild" aria-hidden="true"></i>
    		<span class="parentBar" ng-if="obj.showChild === true && isChildrenShowing(obj.childPage) === false"></span>	
    	  </li>				  
    	</ul>
    	<ul ng-init="child.showGrandChild = false" ng-show="obj.showChild">
    	  <li ng-repeat="child in obj.childPage" ng-click="child.showGrandChild = !child.showGrandChild">
    		<a ng-href="{{child.subPageUrl}}">{{child.subPageTitle}}</a>
    		<i class="fa fa-arrow-down showGrandChild" aria-hidden="true"></i>
    		<ul ng-show="child.showGrandChild">
    		  <li ng-repeat="grandChild in child.grandChild">
    			<a ng-href="{{grandChild.grandChildPageUrl}}">{{grandChild.grandChildPageTitle}}</a>
    		  </li>
    		</ul>
    		<span class="childBar" ng-if="child.showGrandChild === true"></span>
    	  </li>						
    	</ul>	
      </div>
    </div>