8

I'm working on angular data tree recursive table. So the idea is, to throw tree-data (without know the dept of the tree) and render the tree as a table properly with the expandable node. Right now I'm successfully did the tree table by recursively call template to create a table inside the table

Here's the code or you can see it in action here : jsfiddle

<script type="text/ng-template"  id="tree_item.html">

   <tr style="width:100%">
      <td><i class="fa fa-folder-open"></i></td>
      <td>
            {{data.name}}

        <div id="expanded-data">
            <table class="table table-striped" id="nested-table">
                    <div ng-repeat="data in data.nodes" ng-include="'tree_item.html'">     </div>
            </table>
        </div>
    </td>
</tr>

</script>


 <table class="table table-striped">
 <thead>
    <tr>
        <th style="width:30px;"><i ng-click="loadItems()" class="fa fa-refresh blueicon"></i></th>
        <th style="width:auto">Data tree</th>
    </tr>
</thead>
<tbody ng-repeat="data in treeData" ng-include="'tree_item.html'">

</tbody>

</table>

Now I'm stuck with the next step, which is to enable toggle expand & collapse when you click to the folder icon then set child-node to display= none.

I've tried some with ng-switch but with no success. Do you guys have any Ideas how to do this ?

Thank you :)

2

3 Answers 3

6

You can consider using tree-grid.

demo: expandable grid

  <tree-gird tree-data="tree_data"></tree-grid>

Provide a tree structured data, column definition and a property where you want to expand.

Provide a tree structured data, column definition and a property where you want to expand in your controller.

 $scope.tree_data = [
   {Name:"USA",Area:9826675,Population:318212000,TimeZone:"UTC -5 to -10",
  children:[
    {Name:"California", Area:423970,Population:38340000, TimeZone:"Pacific Time",
        children:[
            {Name:"San Francisco", Area:231,Population:837442,TimeZone:"PST"},
            {Name:"Los Angeles", Area:503,Population:3904657,TimeZone:"PST"}
        ]
    },
    {Name:"Illinois", Area:57914,Population:12882135,TimeZone:"Central Time Zone",
        children:[
            {Name:"Chicago", Area:234,Population:2695598,TimeZone:"CST"}
        ]
     }
   ]
  },    
  {Name:"Texas",Area:268581,Population:26448193,TimeZone:"Mountain"}
];

Optionally, you can define column definitions, properties on which you want to use expand and collapse

$scope.col_defs = [
   { field: "Description"},
   { field: "DemographicId", displayName: "Demographic Id"},
   { field: "ParentId", displayName: "Parent Id"},
   { field: "Area"},
   { field: "Population"},
   { field: "TimeZone", displayName: "Time Zone"}
];


$scope.expanding_property = "Name";

details: https://github.com/khan4019/tree-grid-directive

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

Comments

5

Add ng-if here

<div id="expanded-data" data-ng-if="childrenVisible">

and give your tree items a property which defines the visibility of their children. Set the property true or false (if you want false just dont add it by default) by default and implement a toggleChildren function which is called by a click on the toggleButton (the folder)

scope.toggleChildren = function () {
    scope.item.childrenVisible = !scope.item.childrenVisible;
}

EDIT:// Now changing the folder (opened or closed) http://jsfiddle.net/8f3rL/35/

Comments

-2

Use this table, with all CRUD operations and user friendly indentation :-

How to display tree data structure using Angularjs-1 and bootstral html table?


treeDS.html

<!doctype html>
<html>
<head>
    <title>Testbook: Admin App</title>
    <link rel="stylesheet" type="text/css" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
    <style>

        .level0{
            text-indent : 0px;
            color : #428bca;
        }
        .level1{
            text-indent : 20px;
            color : #428bca;
        }
        .level2{
            text-indent : 40px;
            color : #428bca;
        }
        .level3{
            text-indent : 60px;
            color : #428bca;
        }
        .level4{
            text-indent : 80px;
            color : #428bca;
        }
        .level5{
            text-indent : 100px;
            color : #428bca;
        }
        .level6{
            text-indent : 120px;
            color : #428bca;
        }
        .hasDropdown{
            color : #428bca ;
        }
        .noDropdown{
            color : #000000;
        }

        div.tooltip-inner {
            text-align: center;
            -webkit-border-radius: 0px;
            -moz-border-radius: 0px;
            border-radius: 0px;
            margin-bottom: 6px;
            background-color: #505050;
            font-size: 14px;
        }

    </style>

</head>

<body  ng-app="treeDataStructureApp" ng-controller="treeDataStructureCtrl">
    <div class="container-fluid" style="padding-left : 25px;">

        <br><br><br>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <span class="label label-default ng-binding" style="font-size : 25px">
                    Tree Data Structure Implementation Using Bootstrap Table
                </span>
            </div>
        </div>
        <br><br><br>

        <div class="row">
            <div class="col-md-3">
                <button ng-click="addNewNodeAtRootLevel()" type="button" class="btn btn-primary btn-sm">Add New Node</button>
            </div>
        </div>

        <br>
        <div class="row">
            <div class="col-md-12">
                    <table class="table table-hover table-bordered" id="mytable" style="width: 90%;">
                        <caption></caption>
                        <thead>
                        <tr class="info">
                            <th
                            data-toggle="tooltip" data-placement="top" title="click here to expand/close"
                            ng-click="expandAll()">
                            Name

                            <span ng-if="!showAll" class="glyphicon glyphicon-triangle-right" 
                                  aria-hidden="true" 
                                  data-toggle="tooltip" data-placement="top" title="Help: expand all nodes">
                            </span>

                            <span ng-if="showAll" class="glyphicon glyphicon-triangle-bottom" 
                                  aria-hidden="true" 
                                  data-toggle="tooltip" data-placement="top" title="Help: ">
                            </span>

                            </th>
                            <th>Id</th>
                            <th>Operations</th>
                        </tr>
                        </thead>
                        <tbody>
                            <tr ng-repeat="node in nodesTableArr | filter : {isShow : true }" ng-class=" colorBackgroundOfNewnode(node)" >
                                <td ng-class="[hasDropdown(node), selectIndentationClass(node)]">
                                    <span ng-model="node.editable" contenteditable='node.isEditable' ng-click="toggleDropdown(node)"> {{node.name}}</span>
                                </td>
                                <td contenteditable='false'> {{node.id}} </td>
                                <td contenteditable='false'>
                                    <span ng-click="addChildNode(node)" class="glyphicon glyphicon-plus" aria-hidden="true"  
                                    data-toggle="tooltip" data-placement="top" title="Help: add new node under this node edit its name and press save button,save button will show after editing its name">    
                                    </span>

                                    <span ng-click="deleteNode(node, 'concept')" class="glyphicon glyphicon-minus" 
                                          aria-hidden="true" 
                                          data-toggle="tooltip" data-placement="top" title="Help: delete this node, all nodes under it will also be deleted">
                                    </span>

                                    <span ng-click="editNode(node)" class="glyphicon glyphicon-edit" 
                                          aria-hidden="true" 

                                          data-toggle="tooltip" data-placement="top" title="Help: edit name and then press update button, update button will show, after editing its name">
                                    </span>

                                    <button ng-click="saveNewNodeCB (node, false)" type="button" 
                                            class="btn btn-primary btn-xs" ng-show="node.isSaveBtn">save
                                    </button>

                                    <button ng-click="updateNode(node)" type="button" class="btn btn-primary btn-xs" 
                                            ng-show="node.isUpdateBtn">update
                                    </button>

                                    <span ng-medel="operationStatusMessage" ng-show="node.isShowMessage"> 
                                        <small>{{operationStatusMessage}} </small>
                                    </span>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>

        <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js" type="text/javascript"></script>
        <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular-route.min.js" type="text/javascript"></script>
        <script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.1.js" type="text/javascript"></script>
        <script src="treeDS.js" type="text/javascript"></script>  
</body>


treeDS.js file

/** * Created by Aditya on 15/10/2015. */ 'use strict';

var app = angular.module('treeDataStructureApp',[]); app.controller('treeDataStructureCtrl', ['$scope',function ($scope){

// add new node at top level here
    $scope.isAddExamNode = false;
    $scope.addNewNodeAtRootLevel = function(){
        $scope.isAddExamNode = true;
        var newNode = {
                    name: "new node",
                    id : "",
                    level: 0,
                    parent: "root",
                    toggleStatus : false,
                    parentId : -1,
                    isShow: true,
                    isEditable : false,
                    childCount: 0,
                    isSaveBtn : false,
                    isShowMessage : false,
                    type : "exam"
                };
       $scope.nodesTableArr.unshift(newNode);   
    };

    // add new node.
    $scope.operationStatusMessage = "";
    $scope.currentNodeSelected = {};
    var uniqueIdForNewNodes = 0;
    $scope.addChildNode = function(node){
        // add row to table.
        $scope.operationStatusMessage = "";
        $scope.currentNodeSelected = node;
        for (var i = 0; i < $scope.nodesTableArr.length; i++) {
            if($scope.nodesTableArr[i].id == node.id){
                $scope.nodesTableArr.splice(i + 1, 0, {
                    name: "new node",
                    id : uniqueIdForNewNodes,
                    level: node.level + 1,
                    parent: node.name,
                    toggleStatus : false,
                    parentId : node.id,
                    isShow: true,
                    isEditable : false,
                    childCount: 0,
                    isSaveBtn : false,
                    isShowMessage : false,
                    type : "nonExam"
                });
                break;
            }
        }

        uniqueIdForNewNodes += 1;
    };

    $scope.saveNewNode = function(node, isExamNode){
        alert("You can send request on server to add this node here.");
        $scope.saveNewNodeCB();
    };

    $scope.saveNewNodeCB = function(){
            $scope.resetOperationStatusMessage();
            $scope.operationStatusMessage = "node Saved Successfully";
            $scope.currentNodeSelected.isShowMessage = true;
            $scope.currentNodeSelected.isSaveBtn = false;

            // update node id in newly added node object, so that if we add new node under it, it has its valid parent id.
            for(var i = 0 ; i < $scope.nodesTableArr.length ; i++){
                var node = $scope.nodesTableArr[i];
                if(node == $scope.conceptNodeToAdd)
                    node.id = response.data.node;
            }
            alert("send request to server here to save this node.");
    };

    $scope.editNode = function(node){
        $scope.nodeNameInOperation = node.name;
        $scope.operationStatusMessage = "";
        $scope.currentNodeSelected = node;
        var nodeName = prompt("Please enter New node Name", node.name);
        if(nodeName != "" && nodeName != null && node.name != undefined && nodeName != node.name){
          node.name = nodeName;
          if(node.id != ""){
              node.isUpdateBtn = true;
          }
          else{
              node.isSaveBtn = true;
          }
        }
    };

    $scope.updateNode = function(node){
        alert("You can send request on server to update this node.");
        $scope.updateNodeCB();
    };

    $scope.updateNodeCB = function(response){                

        $scope.resetOperationStatusMessage();
        if(true){
            $scope.currentNodeSelected.isShowMessage = true;
            $scope.operationStatusMessage = "node Updated Successfully";
            $scope.currentNodeSelected.isUpdateBtn = false;
        }
    };

    // nodeType = concept/nonConcept
    $scope.nodeToDelete = {};
    $scope.deleteNode = function(node, nodeType){

        $scope.nodeNameInOperation = node.name;
        $scope.nodeTypeForMessage = nodeType;
        $scope.nodeToDelete = node;
        $scope.operationStatusMessage = "";
        $scope.currentNodeSelected = node;
        var message;
        if(node.id == ""){
            for(var i = 0 ; i < $scope.nodesTableArr.length ; i++){
                if($scope.nodesTableArr[i] == $scope.currentNodeSelected)
                    $scope.nodesTableArr.splice(i, 1);
            }
            return 0 ;
        }
        var r = confirm("Warning! Be Carefull! On deletion all nodes under this node will be deleted.\nPress ok to delete node");
        if (r == true) {
            $scope.deleteNodeCB();
            message = "nodes deleted successfully.";
        } else {
            message = "process cancelled.";
        }
        $scope.curPageNo = 1;
    };

    $scope.deleteNodeCB = function(){

      $scope.resetOperationStatusMessage();
      $scope.operationStatusMessage = "node deleted Successfully";
      $scope.currentNodeSelected.isShowMessage = true;

      for(var i = 0 ; i < $scope.nodesTableArr.length ; i++){
          if($scope.nodesTableArr[i] == $scope.currentNodeSelected)
              $scope.nodesTableArr.splice(i, 1);
      }
    };

    $scope.resetOperationStatusMessage = function(){
        for(var i = 0 ; i < $scope.nodesTableArr.length ; i++){
            $scope.nodesTableArr[i].isShowMessage = false;
        }
    };

    // concept nodes related operations ends here


    var node = "";
    $scope.nodesTableArr = [];
    $scope.initializeNodeTreeTable = function(pChildArr){
        var length = pChildArr.length || 0;
        for(var i = 0 ; i < length ; i++){ 

            var exam = pChildArr[i];
            var level = 0;
            var childCount = 0;
            if(exam.children && exam.children.length)
                childCount = exam.children.length;
            $scope.nodesTableArr.push({name : exam.text, id : exam._id, parent : "root", toggleStatus : false,
             parentId : -1, isShow : true, isEditable : false, level : 0, childCount : childCount,
             isSaveBtn : false, isUpdateBtn : false
                });
            if(exam.children != undefined)
            $scope.initializeNodeTreeTableHelper(exam.children, exam.text, exam._id, level);

        }
    };

    $scope.initializeNodeTreeTableHelper = function(pChildArr, pParentName, pPparentId, pLevel){
        var isShowNode = false;
        pLevel = pLevel + 1 ;
        for(var i = 0 ; i < pChildArr.length ; i++){
            var node = pChildArr[i];
            var childCount = node.children != undefined ? node.children.length : 0
            $scope.nodesTableArr.push({name : node.text, id : node._id, parent : pParentName, toggleStatus : false,
             parentId : pPparentId, isShow : isShowNode, isEditable : false, level : pLevel, childCount : childCount,
             isSaveBtn : false, isUpdateBtn : false
            });
            if(node.children != undefined)
                $scope.initializeNodeTreeTableHelper(node.children, node.text, node._id, pLevel)
        }
    };

    $scope.selectedExam = "";
    $scope.renderNodeTreeForExam = "selectedExamNode";
    $scope.renderNodeTreeForExam = function(exam){
        $scope.nodesTableArr = [];
        $scope.selectedExam = exam;
        var selectedExamArr = [exam];
        if($scope.selectedExam == "allExams")
            selectedExamArr = $scope.allExams
        $scope.getAllNodes(selectedExamArr);
    };

    // view related functions.
    $scope.selectIndentationClass = function(node){
        return 'level' + node.level;
    };

    $scope.hasDropdown = function(node){
        if(node.childCount > 0)
            return "hasDropdown";
        else
            return "noDropdown";
    };

    $scope.colorBackgroundOfNewNode = function(node){
        if(node.id == ""){
            return "success";
        }
    };


    $scope.toggleStatus = false;
    $scope.toggleDropdown = function(node){
        node.toggleStatus = node.toggleStatus == true ? false : true;
        $scope.toggleStatus = node.toggleStatus;
        $scope.toggleDropdownHelper(node.id, $scope.toggleStatus );
    };

    $scope.toggleDropdownHelper = function(parentNodeId, toggleStatus ){
        for(var i = 0 ; i < $scope.nodesTableArr.length ; i++) {
            node = $scope.nodesTableArr[i];
            if(node.parentId == parentNodeId) {
                if(toggleStatus == false)
                $scope.toggleDropdownHelper(node.id, toggleStatus);
                $scope.nodesTableArr[i].isShow = toggleStatus;
            }
        }
    };

    $scope.showAll = false;
    $scope.expandAll = function(){
        var  i = 0;
        $scope.showAll = $scope.showAll == true ? false : true;
        if($scope.showAll) {
            for (i = 0; i < $scope.nodesTableArr.length; i++)
                $scope.nodesTableArr[i]['isShow'] = true;
        }
        else {
            for (i = 0; i < $scope.nodesTableArr.length; i++)
                if($scope.nodesTableArr[i]['level'] != 0)
                  $scope.nodesTableArr[i]['isShow'] = false;
        }
    };

    $scope.initializeNodeTreeTable(sampleData.data)
}]);

 var sampleData = {
  "data": [
    {
  "_id": "557569e82a39650f65425104",
  "depth": 0,
  "text": "SBI Clerk",
  "exam": "SBI Clerk",
  "children": [
    {
      "_id": "55c2dee72a3965432eaac6a7",
      "depth": 1,
      "text": "Quantitative Aptitude",
      "exam": "SBI Clerk",
      "children": [
        {
          "_id": "55c2dee72a3965432eaac6a8",
          "depth": 2,
          "text": "Simplification",
          "exam": "SBI Clerk",
          "children": [
            {
              "_id": "55c2dee72a3965432eaac6a9",
              "depth": 3,
              "text": "Bodmas Rule",
              "exam": "SBI Clerk",
              "type": "topic",
              "par": "Bodmas Rule"
            },
            {
              "_id": "55c2dee72a3965432eaac6aa",
              "depth": 3,
              "text": "Surds And Indices",
              "exam": "SBI Clerk",
              "type": "topic",
              "par": "Surds And Indices"
            },
            {
              "_id": "55c2dee82a3965432eaac6ab",
              "depth": 3,
              "text": "Approx Value",
              "exam": "SBI Clerk",
              "type": "topic",
              "par": "Approx Value"
            },
            {
              "_id": "55c2dee82a3965432eaac6ac",
              "depth": 3,
              "text": "Percennodee",
              "exam": "SBI Clerk",
              "type": "topic",
              "par": "Percennodee"
            }
          ],
          "type": "chapter",
          "par": "Simplification"
        }
      ],
      "type": "subject",
      "par": "Quantitative Aptitude"
    }
  ],
  "type": "exam",
  "par": ""
}
  ]
};

jsFiddle : Working Example

http://jsfiddle.net/Aditya199121/y0te91dm/1/

2 Comments

Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.
this problem just have dedicated page, that's why I prefer to give the link. We can include link to working example, which contains all the code.

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.