179 votes

Récursivité dans Angulaire directives

Il ya un couple de populaire récursive angulaire de la directive Q&A, qui viennent tous à l'une des solutions suivantes:

Le premier est le problème que vous ne pouvez pas supprimer précédemment code compilé de façon compréhensible, à moins que vous de gérer le manuel processus de compilation. La deuxième approche a le problème de... de ne pas être une directive et de passer à côté de ses capacités puissantes, mais plus dans l'urgence, il ne peut pas être paramétré de la même manière, une directive peut être; c'est tout simplement lié à une nouvelle instance du contrôleur.

J'ai joué avec manuellement en faisant un angular.bootstrap ou @compile() dans la fonction de lien, mais ça me laisse avec le problème de l'manuellement garder la trace des éléments à supprimer et ajouter.

Est-il un bon moyen d'avoir un paramétrée récursive modèle qui gère l'ajout/suppression d'éléments pour refléter l'état d'exécution? C'est-à-dire, un arbre à ajouter/supprimer un nœud bouton et une entrée de champ dont la valeur est transmise d'un nœud de nœuds enfants. Peut-être une combinaison de la deuxième approche avec enchaîné étendues (mais je n'ai aucune idée de comment le faire)?

317voto

Mark Lagendijk Points 1232

Inspiré par les solutions décrites dans le fil mentionné par @dnc253, j'ai soustrait la récursivité de la 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);
                    }
                }
            };
        }
    };
}]);

Qui est utilisé comme suit:

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) {
            // Use the compile function from the RecursionHelper,
            // And return the linking function(s) which it returns
            return RecursionHelper.compile(element);
        }
    };
});

Voir ce Plunker pour une démo. J'aime cette solution meilleure, parce que:

  1. Vous n'avez pas besoin d'une directive spéciale qui rend votre html moins propre.
  2. La récursivité est la logique disparaît dans le RecursionHelper service, afin de vous garder vos directives propres.

25voto

SunnyShah Points 4088

Manuellement ajouter des éléments et de leur compilation est certainement une approche parfaite. Si vous utilisez ng-repeat, alors vous n'aurez pas à supprimer manuellement les éléments.

Démo: http://jsfiddle.net/KNM4q/113/

.directive('tree', function ($compile) {
return {
    restrict: 'E',
    terminal: true,
    scope: { val: '=', parentData:'=' },
    link: function (scope, element, attrs) {
        var template = '<span>{{val.text}}</span>';
        template += '<button ng-click="deleteMe()" ng-show="val.text">delete</button>';

        if (angular.isArray(scope.val.items)) {
            template += '<ul class="indent"><li ng-repeat="item in val.items"><tree val="item" parent-data="val.items"></tree></li></ul>';
        }
        scope.deleteMe = function(index) {
            if(scope.parentData) {
                var itemIndex = scope.parentData.indexOf(scope.val);
                scope.parentData.splice(itemIndex,1);
            }
            scope.val = {};
        };
        var newElement = angular.element(template);
        $compile(newElement)(scope);
        element.replaceWith(newElement);
    }
}
});

12voto

dnc253 Points 11784

Je ne sais pas pour vous si cette solution est trouvée dans l'un des exemples que vous avez associé ou le même concept de base, mais j'ai eu un besoin de récursive de la directive, et j'ai trouvé un grand, facile solution.

module.directive("recursive", function($compile) {
    return {
        restrict: "EACM",
        priority: 100000,
        compile: function(tElement, tAttr) {
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {
                if(!compiledContents) {
                    compiledContents = $compile(contents);
                }
                iElement.append(
                    compiledContents(scope, 
                                     function(clone) {
                                         return clone; }));
            };
        }
    };
});

module.directive("tree", function() {
    return {
        scope: {tree: '='},
        template: '<p>{{ tree.text }}</p><ul><li ng-repeat="child in tree.children"><recursive><span tree="child"></span></recursive></li></ul>',
        compile: function() {
            return  function() {
            }
        }
    };
});​

Vous devez créer l' recursive directive, puis l'enrouler autour de l'élément qui fait l'appel récursif.

0voto

Jens Points 910

J'ai fini par la création d'un ensemble de directives de base pour la récursivité.

OMI, Il est beaucoup plus basique que la solution trouvée ici, et tout aussi souple, si pas plus, de sorte que nous ne sommes pas liés à l'utilisation d'UL/LI structures, etc... Mais, évidemment, ceux qui font sens pour l'utiliser, cependant, les directives sont pas au courant de ce fait...

Un Super exemple simple serait:

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

La mise en œuvre de la "dx-commencer-avec' une 'dx-connect' est trouvé à: https://github.com/dotJEM/angular-tree

Cela signifie que vous n'avez pas à créer de 8 directives si vous avez besoin de 8 différentes mises en page.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X