É possível fazer uma Tree View com Angular?

Eu estou olhando para exibir dados em uma estrutura de tree em um aplicativo da web. Eu estava esperando usar o Angular para essa tarefa.

Parece que ng-repeat me permitirá percorrer uma lista de nós, mas como posso fazer o aninhamento quando a profundidade de um determinado nó aumenta?

Eu tentei o seguinte código , mas o auto-escape do HTML está impedindo isso de funcionar. Além disso, a tag ul final está no lugar errado.

Tenho certeza de que estou lidando com esse problema da maneira errada.

Alguma ideia?

Dê uma olhada neste violino

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

Atualizado: http://jsfiddle.net/animaxf/uXbn6/4779/

Isso deve lhe dar uma boa idéia de como exibir uma tree like structure usando angular. É meio que usar recursion em html!

Se você estiver usando o Bootstrap CSS …

Eu criei um controle de tree reutilizável simples (diretiva) para AngularJS baseado em uma lista de “nav” de Bootstrap. Eu adicionei indentação extra, icons e animação. Atributos HTML são usados ​​para configuração.

Não usa recursion.

Eu chamei de angular-bootstrap-nav-tree (nome cativante, você não acha?)

Há um exemplo aqui e a fonte está aqui .

Ao fazer algo assim, a melhor solução é uma diretiva recursiva. No entanto, quando você faz essa diretiva, você descobre que o AngularJS entra em um loop infinito.

A solução para isso é permitir que a diretiva remova o elemento durante o evento de compilation e compile-os manualmente e inclua-os nos events de link.

Eu descobri isso neste tópico e abstracionei essa funcionalidade em um serviço .

 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); } } }; } }; }]); 

Com este serviço, você pode facilmente criar uma diretiva de tree (ou outras diretivas recursivas). Aqui está um exemplo de uma diretiva de tree:

 module.directive("tree", function(RecursionHelper) { return { restrict: "E", scope: {family: '='}, template: '

{{ family.name }}

'+ '
    ' + '
  • ' + '' + '
  • ' + '
', compile: function(element) { return RecursionHelper.compile(element); } }; });

Veja este Plunker para uma demonstração. Eu gosto mais desta solução porque:

  1. Você não precisa de uma diretiva especial que torne o seu html menos limpo.
  2. A lógica de recursion é abstraída no serviço RecursionHelper, portanto, você mantém suas diretivas limpas.

Atualização: Adicionado suporte para funções de vinculação personalizadas.

angular-ui-tree parece fazer um bom trabalho para mim

Aqui está um exemplo usando uma diretiva recursiva: http://jsfiddle.net/n8dPm/ Extraído de https://groups.google.com/forum/#!topic/angular/vswXTes_FtM

 module.directive("tree", function($compile) { return { restrict: "E", scope: {family: '='}, template: '

{{ family.name }}

'+ '
    ' + '
  • ' + '' + '
  • ' + '
', 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); }); }; } }; });

Este parece um pouco mais completo: https://github.com/dump247/angular.tree

Outro exemplo baseado na fonte original , com uma estrutura de tree de exemplo já em vigor (mais fácil de ver como funciona o IMO) e um filtro para pesquisar a tree:

JSFiddle

Tantas ótimas soluções, mas sinto que todas elas de uma forma ou de outra complicam demais as coisas.

Eu queria criar algo que recriava a simplicidade do awnser do @Mark Lagendijk, mas sem definir um template na diretiva, mas deixaria o “usuário” criar o template em HTML …

Com ideias tiradas de https://github.com/stackfull/angular-tree-repeat etc … acabei criando o projeto: https://github.com/dotJEM/angular-tree

Que permite que você construa sua tree como:

 
  • {{ node.name }}

    O que para mim é mais limpo do que ter que criar múltiplas diretivas para trees estruturadas de forma diferente … Em essência, chamar uma tree acima é um pouco falso, ela tira muito mais do que o “ganger” de “modelos recursivos”, mas nos permite defina o modelo onde precisamos da tree.

    (você poderia fazer isso com um modelo baseado em tag de script, mas ele ainda tem que ficar fora do nó da tree, e ainda parece um pouco yuk …)

    Deixado aqui por apenas mais uma escolha …

    Você pode tentar com amostra Angular-Tree-DnD com Angular-Ui-Tree, mas eu editei, compatibilidade com tabela, grade, lista.

    • Arrastar e Soltar
    • Diretiva de function estendida para lista (next, prev, getChildren, …)
    • Filtrar dados.
    • OrderBy (ver)

    Com base na resposta do @ganaraj e na resposta do @ dnc253, acabei de fazer uma “diretiva” simples para a estrutura de tree ter o recurso de selecionar, adicionar, excluir e editar.

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

    HTML:

      

    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: '', controller: function($scope){ this.select = function(data){ if($scope.selected){ $scope.selected.selected = false; } data.selected = true; $scope.selected = data; }; }, scope: { data: '=', } } }); 

    Sim, definitivamente é possível. A questão aqui provavelmente assume Angular 1.x, mas para referência futura estou incluindo um exemplo Angular 2:

    Conceitualmente, tudo o que você precisa fazer é criar um modelo recursivo:

     
    • {{ dir.name }}
        {{file}}

    Você então liga um object de tree ao modelo e deixa o Angular trabalhar sua mágica. Este conceito é obviamente aplicável ao Angular 1.x também.

    Aqui está um exemplo completo: http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0

    Você pode usar o injetor de recursion angular para isso: https://github.com/knyga/angular-recursion-injector

    Permite que você faça aninhamento ilimitado com condicionamento. Recompila somente se necessário e compila apenas elementos corretos. Nenhuma mágica no código.

     
    {{name}}

    Uma das coisas que permite que ele funcione mais rápido e mais simples, então as outras soluções são o sufixo “–recursion”.

    Quando a estrutura da tree é grande, o Angular (até 1.4.x) fica muito lento na renderização de um modelo recursivo. Depois de tentar várias dessas sugestões, acabei criando uma string HTML simples e usando ng-bind-html para exibi-la. Claro, este não é o caminho para usar resources angulares

    Uma function recursiva básica é mostrada aqui (com o mínimo de HTML):

     function menu_tree(menu, prefix) { var html = '
    ' + prefix + menu.menu_name + ' - ' + menu.menu_desc + '
    \n'; if (!menu.items) return html; prefix += menu.menu_name + '/'; for (var i=0; i

    No modelo, só precisa desta linha:

     

    Isso ignora toda a vinculação de dados do Angular e simplesmente exibe o HTML em uma fração do tempo dos methods de modelo recursivo.

    Com uma estrutura de menus como essa (uma tree de arquivos parcial de um sistema de arquivos 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'} ] } 

    A saída é:

     - 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 

    Não é complicado.

     
    col 1 col 2 col 3
    {{item.id}} {{item.fname}} {{item.lname}}
    {{children.id}} {{children.fname}}

    código do controlador:

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