J'ai quelque chose qui fonctionne maintenant. Cela fonctionne de la manière que j'ai décrite dans mon commentaire sur ma question ci-dessus. Juste pour récapituler, j'utilise 2 directives personnalisées, toutes deux basées sur ng-repeat. Le premier construit une structure de table plate avec des valeurs littérales dans l'attribut de classe pour indiquer la profondeur. La seconde directive utilise cette information de profondeur pour répéter l'élément de remplissage le nombre approprié de fois. Les deux répéteurs utilisent la transclusion pour minimiser les restrictions/dépendances sur le code HTML. Le seul inconvénient est que le deuxième répéteur a besoin de l'information de profondeur pour être quelque part dans son ascendance dans le DOM.
Voici ce que le modèle HTML ressemble pour produire un arbre table de la structure que j'ai décrit dans ma question:
<table class="treetable" style="width: 40%;">
<tr class="treetable-node" my-repeat="item in things"
my-repeat-children="children">
<td>
<span my-repeat-padding class="treetable-node-indent"></span>
{{ item.name }}
</td>
<td>
{{ item.type }}
</td>
</tr>
</table>
I sur la base de mon répéteur personnalisé sur this exemple. Ce qui rend le mien différent, c'est que vous pouvez spécifier une propriété "children" et la logique de la directive vérifiera cette propriété sur chaque élément, et descendra récursivement dedans si elle est là - bien qu'elle attache tous les nouveaux éléments au même parent, me donnant le structure plate.
Il est probablement assez inefficace, et pourrait utiliser une certaine optimisation - comme le code dans le lien que j'ai fourni, il reconstruit tout quand il y a un changement.
myApp.directive("myRepeat", function($parse) {
return {
restrict: "A",
replace: true,
transclude: "element",
link: function (scope, element, attrs, controller, transclude) {
match = attrs.myRepeat.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/);
itemName = match[1];
collectionName = match[2];
childCollectionName = attrs["myRepeatChildren"];
parentElement = element.parent();
elements = [];
scope.$watchCollection(collectionName, function(collection) {
var i, block, childScope;
if (elements.length > 0) {
for(i=0; i<elements.length; i++) {
elements[i].el.remove();
elements[i].scope.$destroy();
};
elements = [];
}
var buildHtml = function(parent, itemList, depth=0) {
for (var i=0; i<itemList.length; i++) {
childScope = scope.$new();
childScope[itemName] = itemList[i];
transclude(childScope, function(clone) {
parentElement.append(clone);
block = {};
block.el = clone;
block.scope = childScope;
block.el.addClass("depth-" + depth);
elements.push(block);
});
/*Recurse if this item has children,
adding the sub-elements to the same
parent so we end up with a flat list*/
if(childCollectionName in itemList[i]) {
buildHtml(parentElement,
itemList[i][childCollectionName],
depth+1);
}
}
}
for (i=0; i<collection.length; i++) {
buildHtml(parentElement, collection);
}
});
}
};
});
La seconde est un peu hacky. C'est aussi un répéteur de transclusion. Il recherche vers le haut dans le DOM pour l'attribut de classe de profondeur, analyse la valeur de profondeur, et s'y ajoute plusieurs fois dans le parent. Donc, cela dépend totalement de la classe de profondeur définie par la première directive. Il y a probablement de meilleures façons de le faire. Je n'ai pas non plus pris la peine de mettre en place une montre ou quelque chose comme ça, car c'est purement cosmétique.
myApp.directive("myRepeatPadding", function($parse) {
return {
restrict: "A",
replace: true,
transclude: "element",
terminal: true,
link: function (scope, element, attrs, controller, transclude) {
var getDepth = function(element) {
classes = element.attr("class");
if (classes) {
match = classes.match(/depth-([\d+])/);
if(match.length > 0) {
return match[1];
} else {
return getDepth(element.parent());
}
} else {
return getDepth(element.parent());
}
}
depth = getDepth(element);
for (var i=0; i<depth; i++) {
transclude(scope, function(clone) {
element.parent().prepend(clone);
block = {};
block.el = clone;
block.scope = scope;
elements.push(block);
});
}
}
};
});
C'est loin d'être parfait et je vais devoir passer du temps à l'améliorer. Je ne suis même pas sûr que le HTML que je produise est le meilleur moyen d'obtenir l'apparence que je veux - mais il rend le rendu que je veux tout en gardant le modèle HTML raisonnablement élégant.
quelle raison cela doit-il être une table? vous pouvez essayer d'étendre une directive comme [angular-json-tree] (https://github.com/awendland/angular-json-tree) pour permettre l'édition – haxxxton
J'ai besoin de plusieurs colonnes. Je ne les ai pas montrés dans mon exemple pour la brièveté. –
Que cherchez-vous à réaliser avec les colonnes supplémentaires? :) boutons pour éditer/supprimer/dupliquer? vous pouvez probablement gérer la même chose avec ui-popover ou un modal si oui – haxxxton