Est-il possible de faire une Arborescence Angulaire?

je cherche à afficher des données dans une structure arborescente dans une application web. J'espérais utiliser Angulaire pour cette tâche.

ressemble à ng-repeat me permettra d'itérer à travers une liste de noeuds, mais comment puis-je alors faire la nidification quand la profondeur d'un noeud donné augmente?

j'ai essayé le suivant le code , mais l'auto-évasion du HTML empêche cela de fonctionner. En plus, l'étiquette ul n'est pas au bon endroit.

je suis presque sûr que je vais au sujet de ce problème entièrement de la mauvaise façon.

des idées?

168
demandé sur Jon Abrams 2012-08-08 01:38:35

14 réponses

Regardez ce violon

Original: http://jsfiddle.net/brendanowen/uXbn6/8 /

mise à jour: http://jsfiddle.net/animaxf/uXbn6/4779 /

cela devrait vous donner une bonne idée de la façon d'afficher un tree like structure en utilisant l'angle. C'est une sorte d'utilisation de la récursion en html!

221
répondu ganaraj 2018-03-21 10:31:23

si vous utilisez Bootstrap CSS...

j'ai créé une simple commande d'arbre réutilisable (directive) pour AngularJS basée sur une liste de" nav " Bootstrap. J'ai ajouté une indentation supplémentaire, des icônes et de l'animation. Les attributs HTML sont utilisés pour la configuration.

il n'utilise pas la récursion.

je l'ai appelé angulaires-bootstrap-nav-arbre ( nom accrocheur, vous ne pensez pas? )

il y a un exemple ici , et la source est ici .

77
répondu Nick Perkins 2013-11-03 16:42:50

en faisant quelque chose comme ceci la meilleure solution est une directive récursive. Cependant, quand vous faites une telle directive, vous découvrez que AngularJS se trouve dans une boucle sans fin.

la solution pour cela est de laisser la directive supprimer l'élément lors de l'événement de compilation, et compiler et ajouter manuellement l'élément lors des événements de lien.

j'ai découvert à ce sujet dans ce fil , et résumé cette fonctionnalité dans un service .

module.factory('RecursionHelper', ['$compile', function($compile){
    return {
        /**
         * Manually compiles the element, fixing the recursion loop.
         * @param element
         * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
         * @returns An object containing the linking functions.
         */
        compile: function(element, link){
            // Normalize the link parameter
            if(angular.isFunction(link)){
                link = { post: link };
            }

            // Break the recursion loop by removing the contents
            var contents = element.contents().remove();
            var compiledContents;
            return {
                pre: (link && link.pre) ? link.pre : null,
                /**
                 * Compiles and re-adds the contents
                 */
                post: function(scope, element){
                    // Compile the contents
                    if(!compiledContents){
                        compiledContents = $compile(contents);
                    }
                    // Re-add the compiled contents to the element
                    compiledContents(scope, function(clone){
                        element.append(clone);
                    });

                    // Call the post-linking function, if any
                    if(link && link.post){
                        link.post.apply(null, arguments);
                    }
                }
            };
        }
    };
}]);

avec ce service, vous pouvez facilement créer une directive tree (ou d'autres directives récursives). Voici un exemple de directive tree:

module.directive("tree", function(RecursionHelper) {
    return {
        restrict: "E",
        scope: {family: '='},
        template: 
            '<p>{{ family.name }}</p>'+
            '<ul>' + 
                '<li ng-repeat="child in family.children">' + 
                    '<tree family="child"></tree>' +
                '</li>' +
            '</ul>',
        compile: function(element) {
            return RecursionHelper.compile(element);
        }
    };
});

voir ce Plunker pour une démo. Je préfère cette solution parce que:

  1. vous n'avez pas besoin d'une directive spéciale qui rend votre html moins propre.
  2. la logique de la récursion est abstracted away dans le service D'Helper de récursion, donc vous gardez vos directives propres.

mise à jour: prise en charge D'une fonction de liaison personnalisée.

34
répondu Mark Lagendijk 2014-02-08 19:18:14

angular-ui-arbre semble faire du bon travail pour moi

16
répondu Kalyanaraman Santhanam 2015-12-09 16:50:27

voici un exemple d'utilisation d'une directive récursive: http://jsfiddle.net/n8dPm / Extrait de https://groups.google.com/forum/#!topic/angulaire/vswXTes_FtM

module.directive("tree", function($compile) {
return {
    restrict: "E",
    scope: {family: '='},
    template: 
        '<p>{{ family.name }}</p>'+
        '<ul>' + 
            '<li ng-repeat="child in family.children">' + 
                '<tree family="child"></tree>' +
            '</li>' +
        '</ul>',
    compile: function(tElement, tAttr) {
        var contents = tElement.contents().remove();
        var compiledContents;
        return function(scope, iElement, iAttr) {
            if(!compiledContents) {
                compiledContents = $compile(contents);
            }
            compiledContents(scope, function(clone, scope) {
                     iElement.append(clone); 
            });
        };
    }
};
});
14
répondu savagepanda 2013-05-27 21:28:43

celui-ci semble un peu plus complet: https://github.com/dump247/angular.tree

12
répondu moskiteau 2013-03-04 16:53:47

un autre exemple basé sur le source originale , avec une structure d'arbre échantillon déjà en place (plus facile à voir comment cela fonctionne IMO) et un filtre pour rechercher l'arbre:

JSFiddle

5
répondu GFoley83 2013-03-23 23:32:41

tant de bonnes solutions, mais je pense qu'elles compliquent un peu les choses d'une façon ou d'une autre.

je voulais créer quelque chose qui recréait la simplicité de l'awnser de @Mark Lagendijk, mais sans qu'il définisse un modèle dans la directive, mais plutôt laisser l '"Utilisateur" créer le modèle en HTML...

avec des idées tirées de https://github.com/stackfull/angular-tree-repeat etc... J'ai fini avec la création d' le projet: https://github.com/dotJEM/angular-tree

qui vous permet de construire votre arbre comme:

<ul dx-start-with="rootNode">
  <li ng-repeat="node in $dxPrior.nodes">
    {{ node.name }}
    <ul dx-connect="node"/>
  </li>
</ul>

qui est pour moi plus propre que d'avoir à créer plusieurs directives pour des arbres différemment structurés.... En substance, appeler l'arbre ci-dessus est un peu faux, Il tire beaucoup plus de l'awnser de @ganaraj de "gabarits récursifs", mais nous permet de définir le gabarit où nous avons besoin de l'arbre.

(vous pourriez faire cela avec un template basé sur une étiquette de script, mais il doit toujours s'asseoir juste à l'extérieur du noeud d'arbre réel, et il se sent encore juste un peu yuk...)

est parti d'ici pour un autre choix...

4
répondu Jens 2015-02-10 09:25:27

Vous pouvez essayer avec Angulaire de l'Arbre-Mdn échantillon avec Angular-Ui-Arbre, mais j'ai édité, la compatibilité avec table, grille, liste.

  • Mesure " Drag & Drop
  • extension de la fonction de directive pour la liste (next, prev, getChildren,...)
  • données du filtre.
  • OrderBy (ver)
3
répondu Nguyễn Thiện Hùng 2015-05-29 16:31:37

basé sur answer de @ganaraj , et s answer de @dnc253 , je viens de faire une simple "directive" pour la structure de l'arbre ayant la fonction de sélection, d'ajout, de suppression et d'édition.

Jsfiddle: http://jsfiddle.net/yoshiokatsuneo/9dzsms7y /

HTML:

<script type="text/ng-template" id="tree_item_renderer.html">
    <div class="node"  ng-class="{selected: data.selected}" ng-click="select(data)">
        <span ng-click="data.hide=!data.hide" style="display:inline-block; width:10px;">
            <span ng-show="data.hide && data.nodes.length > 0" class="fa fa-caret-right">+</span>
            <span ng-show="!data.hide && data.nodes.length > 0" class="fa fa-caret-down">-</span>
        </span>
        <span ng-show="!data.editting" ng-dblclick="edit($event)" >{{data.name}}</span>
        <span ng-show="data.editting"><input ng-model="data.name" ng-blur="unedit()" ng-focus="f()"></input></span>
        <button ng-click="add(data)">Add node</button>
        <button ng-click="delete(data)" ng-show="data.parent">Delete node</button>
    </div>
    <ul ng-show="!data.hide" style="list-style-type: none; padding-left: 15px">
        <li ng-repeat="data in data.nodes">
            <recursive><sub-tree data="data"></sub-tree></recursive>
        </li>
    </ul>
</script>
<ul ng-app="Application" style="list-style-type: none; padding-left: 0">
    <tree data='{name: "Node", nodes: [],show:true}'></tree>
</ul>

JavaScript:

angular.module("myApp",[]);

/* https://stackoverflow.com/a/14657310/1309218 */
angular.module("myApp").
directive("recursive", function($compile) {
    return {
        restrict: "EACM",
        require: '^tree',
        priority: 100000,

        compile: function(tElement, tAttr) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents);
                }
                compiledContents(scope, 
                                     function(clone) {
                         iElement.append(clone);
                                         });
            };
        }
    };
});

angular.module("myApp").
directive("subTree", function($timeout) {
    return {
        restrict: 'EA',
        require: '^tree',
        templateUrl: 'tree_item_renderer.html',
        scope: {
            data: '=',
        },
        link: function(scope, element, attrs, treeCtrl) {
            scope.select = function(){
                treeCtrl.select(scope.data);
            };
            scope.delete = function() {
                scope.data.parent.nodes.splice(scope.data.parent.nodes.indexOf(scope.data), 1);
            };
            scope.add = function() {
                var post = scope.data.nodes.length + 1;
                var newName = scope.data.name + '-' + post;
                scope.data.nodes.push({name: newName,nodes: [],show:true, parent: scope.data});
            };
            scope.edit = function(event){
                scope.data.editting = true;
                $timeout(function(){event.target.parentNode.querySelector('input').focus();});
            };
            scope.unedit = function(){
                scope.data.editting = false;
            };

        }
    };
});


angular.module("myApp").
directive("tree", function(){
    return {
        restrict: 'EA',
        template: '<sub-tree data="data" root="data"></sub-tree>',
        controller: function($scope){
            this.select = function(data){
                if($scope.selected){
                    $scope.selected.selected = false;
                }
                data.selected = true;
                $scope.selected = data;
            };
        },
        scope: {
            data: '=',
        }
    }
});
2
répondu Tsuneo Yoshioka 2017-05-23 11:47:12

Oui, il est certainement possible. La question ici suppose probablement 1 angulaire.x, mais pour référence future j'inclus un exemple 2 angulaire:

conceptuellement, tout ce que vous avez à faire est de créer un modèle récursif:

<ul>
    <li *for="#dir of directories">

        <span><input type="checkbox" [checked]="dir.checked" (click)="dir.check()"    /></span> 
        <span (click)="dir.toggle()">{{ dir.name }}</span>

        <div *if="dir.expanded">
            <ul *for="#file of dir.files">
                {{file}}
            </ul>
            <tree-view [directories]="dir.directories"></tree-view>
        </div>
    </li>
</ul>

vous liez alors un objet arborescent au Modèle et laissez la magie de L'angle agir. Ce concept est évidemment applicable à L'Angle 1.x ainsi.

voici un exemple complet: http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0

0
répondu TGH 2015-05-30 20:55:10

vous pouvez utiliser angular-recursion-injector pour cela: https://github.com/knyga/angular-recursion-injector

vous permet de faire un emboîtement illimité avec conditionnement. Ne recompilation que si nécessaire et ne compile que les bons éléments. Pas de magie en code.

<div class="node">
  <span>{{name}}</span>

  <node--recursion recursion-if="subNode" ng-model="subNode"></node--recursion>
</div>

une des choses qui lui permet de fonctionner plus rapidement et plus simplement que les autres solutions est le suffixe" --récursion".

0
répondu Oleksandr Knyga 2015-06-12 13:34:44

lorsque la structure de l'arbre est grande, angulaire (jusqu'à 1,4.x) devient très lent à rendre un modèle récursif. Après avoir essayé un certain nombre de ces suggestions, j'ai fini par créer une chaîne HTML simple et en utilisant ng-bind-html pour l'afficher. Bien sûr, ce n'est pas la façon d'utiliser les caractéristiques angulaires

une fonction récursive Bare-bones est montrée ici (avec HTML minimal):

function menu_tree(menu, prefix) {
    var html = '<div>' + prefix + menu.menu_name + ' - ' + menu.menu_desc + '</div>\n';
    if (!menu.items) return html;
    prefix += menu.menu_name + '/';
    for (var i=0; i<menu.items.length; ++i) {
        var item = menu.items[i];
        html += menu_tree(item, prefix);
    }
    return html;
}
// Generate the tree view and tell Angular to trust this HTML
$scope.html_menu = $sce.trustAsHtml(menu_tree(menu, ''));

dans le modèle, il n'a besoin que de cette ligne:

<div ng-bind-html="html_menu"></div>

ceci contourne toute la liaison de données D'Angular et affiche simplement le HTML dans une fraction du temps des méthodes de gabarit récursives.

avec une structure de menu comme celle-ci (un arbre de fichiers partiel D'un système de fichiers Linux):

menu = {menu_name: '', menu_desc: 'root', items: [
            {menu_name: 'bin', menu_desc: 'Essential command binaries', items: [
                {menu_name: 'arch', menu_desc: 'print machine architecture'},
                {menu_name: 'bash', menu_desc: 'GNU Bourne-Again SHell'},
                {menu_name: 'cat', menu_desc: 'concatenate and print files'},
                {menu_name: 'date', menu_desc: 'display or set date and time'},
                {menu_name: '...', menu_desc: 'other files'}
            ]},
            {menu_name: 'boot', menu_desc: 'Static files of the boot loader'},
            {menu_name: 'dev', menu_desc: 'Device files'},
            {menu_name: 'etc', menu_desc: 'Host-specific system configuration'},
            {menu_name: 'lib', menu_desc: 'Essential shared libraries and kernel modules'},
            {menu_name: 'media', menu_desc: 'Mount point for removable media'},
            {menu_name: 'mnt', menu_desc: 'Mount point for mounting a filesystem temporarily'},
            {menu_name: 'opt', menu_desc: 'Add-on application software packages'},
            {menu_name: 'sbin', menu_desc: 'Essential system binaries'},
            {menu_name: 'srv', menu_desc: 'Data for services provided by this system'},
            {menu_name: 'tmp', menu_desc: 'Temporary files'},
            {menu_name: 'usr', menu_desc: 'Secondary hierarchy', items: [
                {menu_name: 'bin', menu_desc: 'user utilities and applications'},
                {menu_name: 'include', menu_desc: ''},
                {menu_name: 'local', menu_desc: '', items: [
                    {menu_name: 'bin', menu_desc: 'local user binaries'},
                    {menu_name: 'games', menu_desc: 'local user games'}
                ]},
                {menu_name: 'sbin', menu_desc: ''},
                {menu_name: 'share', menu_desc: ''},
                {menu_name: '...', menu_desc: 'other files'}
            ]},
            {menu_name: 'var', menu_desc: 'Variable data'}
        ]
       }

la sortie devient:

- root
/bin - Essential command binaries
/bin/arch - print machine architecture
/bin/bash - GNU Bourne-Again SHell
/bin/cat - concatenate and print files
/bin/date - display or set date and time
/bin/... - other files
/boot - Static files of the boot loader
/dev - Device files
/etc - Host-specific system configuration
/lib - Essential shared libraries and kernel modules
/media - Mount point for removable media
/mnt - Mount point for mounting a filesystem temporarily
/opt - Add-on application software packages
/sbin - Essential system binaries
/srv - Data for services provided by this system
/tmp - Temporary files
/usr - Secondary hierarchy
/usr/bin - user utilities and applications
/usr/include -
/usr/local -
/usr/local/bin - local user binaries
/usr/local/games - local user games
/usr/sbin -
/usr/share -
/usr/... - other files
/var - Variable data
0
répondu Brent Washburne 2015-09-16 21:44:15

pas compliqué.

<div ng-app="Application" ng-controller="TreeController">
    <table>
        <thead>
            <tr>
                <th>col 1</th>
                <th>col 2</th>
                <th>col 3</th>
            </tr>
        </thead>
        <tbody ng-repeat="item in tree">
            <tr>
                <td>{{item.id}}</td>
                <td>{{item.fname}}</td>
                <td>{{item.lname}}</td>
            </tr>
            <tr ng-repeat="children in item.child">
                <td style="padding-left:15px;">{{children.id}}</td>
                <td>{{children.fname}}</td>
            </tr>
        </tbody>
     </table>
</div>

code du contrôleur:

angular.module("myApp", []).
controller("TreeController", ['$scope', function ($scope) {
    $scope.tree = [{
        id: 1,
        fname: "tree",
        child: [{
            id: 1,
            fname: "example"
        }],
        lname: "grid"
    }];


}]);
-2
répondu MBK 2015-11-23 14:02:35