角度指令中的递归

2022-08-30 01:51:15

有几个流行的递归角度指令问答,它们都归结为以下解决方案之一:

第一个问题有一个问题,即除非您能够全面管理手动编译过程,否则无法删除以前编译的代码。第二种方法存在以下问题:不是指令并错过其强大的功能,但更紧迫的是,它不能像指令那样进行参数化;它只是绑定到一个新的控制器实例。

我一直在手动执行或链接功能,但这让我遇到了手动跟踪要删除和添加的元素的问题。angular.bootstrap@compile()

有没有一种好方法可以创建一个参数化的递归模式来管理添加/删除元素以反映运行时状态?也就是说,一个带有添加/删除节点按钮和一些输入字段的树,其值向下传递节点的子节点。也许是第二种方法与链式范围的组合(但我不知道如何做到这一点)?


答案 1

受到@dnc253提到的线程中描述的解决方案的启发,我将递归功能抽象为服务

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

其用法如下:

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

请参阅此 Plunker 以获取演示。我最喜欢这个解决方案,因为:

  1. 你不需要一个特殊的指令来让你的html不那么干净。
  2. 递归逻辑被抽象到递归帮助程序服务中,因此您可以保持指令整洁。

更新:从Angular 1.5.x开始,不需要更多的技巧,但仅适用于模板,而不适用于templateUrl


答案 2

手动添加元素并编译它们绝对是一种完美的方法。如果使用ng-repeat,则不必手动删除元素。

演示: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);
    }
}
});