Recursion in Angular directives

前端 未结 9 1501
不思量自难忘°
不思量自难忘° 2020-11-22 04:24

There are a couple of popular recursive angular directive Q&A\'s out there, which all come down to one of the following solutions:

  • manually incrementally \
相关标签:
9条回答
  • 2020-11-22 04:51

    Manually adding elements and compiling them is definitely a perfect approach. If you use ng-repeat then you will not have to manually remove elements.

    Demo: 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);
        }
    }
    });
    
    0 讨论(0)
  • 2020-11-22 04:53

    Inspired by the solutions described in the thread mentioned by @dnc253, I abstracted the recursion functionality into a 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);
                        }
                    }
                };
            }
        };
    }]);
    

    Which is used as follows:

    module.directive("tree", ["RecursionHelper", 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);
            }
        };
    }]);
    

    See this Plunker for a demo. I like this solution best because:

    1. You don't need an special directive which makes your html less clean.
    2. The recursion logic is abstracted away into the RecursionHelper service, so you keep your directives clean.

    Update: As of Angular 1.5.x, no more tricks are required, but works only with template, not with templateUrl

    0 讨论(0)
  • 2020-11-22 04:54

    You can use angular-recursion-injector for that: https://github.com/knyga/angular-recursion-injector

    Allows you to do unlimited depth nesting with conditioning. Does recompilation only if needed and compiles only right elements. No magic in code.

    <div class="node">
      <span>{{name}}</span>
    
      <node--recursion recursion-if="subNode" ng-model="subNode"></node--recursion>
    </div>
    

    One of the things that allows it to work faster and simpler then the other solutions is "--recursion" suffix.

    0 讨论(0)
提交回复
热议问题