AngularJS: tabela de árvore recursiva expansível

Estou a trabalhar na tabela recursiva da árvore de dados angular. Então a idéia é, jogar dados de árvore (sem conhecer o dept da árvore) e renderizar a árvore como uma tabela corretamente com o nó expansível. Neste momento, fiz com sucesso a tabela de árvore ao chamar o modelo recursivamente para criar uma tabela dentro da tabela

Aqui está o código ou você pode vê-lo em ação aqui: 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>

Agora estou preso com o próximo passo, que é activar a opção Expandir e fechar quando você carregue no ícone da pasta e depois configure o nó-filho para mostrar = nenhum.

Tentei alguns com o ng-switch, mas sem sucesso. Vocês têm alguma ideia de como fazer isto ?

Obrigado.
Author: Pung Worathiti Manosroi, 2014-03-27

3 answers

Adicionar ng-se aqui

<div id="expanded-data" data-ng-if="childrenVisible">
E dê aos seus itens da árvore uma propriedade que defina a visibilidade dos seus filhos. Configure a propriedade true ou false (se quiser o false apenas não a Adicione por omissão) por omissão e implemente uma função toggleChildren que é chamada por um clique no botão toggleButton (a pasta)
scope.toggleChildren = function () {
    scope.item.childrenVisible = !scope.item.childrenVisible;
}

Editar: / / agora a mudar a pasta (aberta ou fechada) http://jsfiddle.net/8f3rL/35/

 4
Author: John Smith, 2014-03-27 11:03:45

Podes considerar usar a grelha das árvores.

Demo: grelha expansível

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

Forneça uma árvore de dados estruturados, a definição de colunas e uma propriedade onde deseja expandir.

Forneça uma árvore de dados estruturados, a definição de colunas e uma propriedade onde você deseja expandir no seu controlador.

 $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"}
];
Opcionalmente, poderá definir definições de colunas, propriedades nas quais deseja usar expandir e colapsar
$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";

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

 6
Author: KhanSharp, 2014-06-17 14:17:10

Utilize esta tabela, com todas as operações CRUD e indentação amigável: -

Como mostrar a estrutura de dados da árvore usando Angularjs-1 e a tabela html bootstral?


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.ficheiro js

/** * Criado por Aditya em 15/10/2015. */ "use strict";

Aplicação Var = angular.módulo ("treedatastractureapp", []); aplicacao.controlador ("treedatastractrl", ["$scope", função ($scope) {[[8]}

// 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: Exemplo De Trabalho

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

 -2
Author: aditya, 2017-05-23 11:55:07