AngularJS: 理解递归指令

15

我在这里找到了一个很棒的树形指令。原链接:http://jsfiddle.net/n8dPm/

我通过查看其他几篇SO问题(12)尝试理解它的工作原理。但我并没有很清楚地理解递归调用渲染树形指令的方式,主要是compile函数。

  1. compile函数什么时候被全部调用?
  2. 什么时候将$compile函数缓存到变量compiledContents中(这是link函数吗?),什么时候将它附加到HTML文档中?为什么不总是附加?

--

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); 
                });
            };
        },
1个回答

32

Ng网站拥有非常优秀的文档(在我看来是最好的之一)。其中关于启动和运行循环的概述非常有帮助:http://docs.angularjs.org/guide/concepts

从高层次上讲,当Ng首次启动时,它会编译DOM,从ng-app所在位置开始(被Ng视为另一个指令)。这意味着它会遍历元素并查找需要链接到$rootScope(由编译/链接过程设置的原型继承链中所有作用域的根)的指令和表达式。如果是指令,则也对其进行编译处理。编译处理将所有在HTML中发现的Ng指令按其分配的优先级排序或假定优先级为零。当它已经将它们全部排序后,就会执行指令的编译函数,该函数返回链接函数。在上面的示例中,有两个show链接函数,我将在下面进行注释,并将其与此解释相关联。链接函数还以传输对象的形式获得了指令所在属性、类或元素中的HTML。

执行链接函数以链接作用域和指令,同时生成视图。这可能包括HTML/传输对象,以便在指令ng-transclude处添加(该模板将应用相同的过程,其模板是传输对象)。

因此,这里是我对以上略作纠正的自定义指令的注释:

module.directive("tree", function($compile) {
    //Here is the Directive Definition Object being returned 
    //which is one of the two options for creating a custom directive
    //http://docs.angularjs.org/guide/directive
    return {
        restrict: "E",
        //We are stating here the HTML in the element the directive is applied to is going to be given to
        //the template with a ng-transclude directive to be compiled when processing the directive
        transclude: true,
        scope: {family: '='},
        template:       
            '<ul>' + 
                //Here we have one of the ng-transclude directives that will be give the HTML in the 
                //element the directive is applied to
                '<li ng-transclude></li>' +
                '<li ng-repeat="child in family.children">' +
                    //Here is another ng-transclude directive which will be given the same transclude HTML as
                    //above instance
                    //Notice that there is also another directive, 'tree', which is same type of directive this 
                    //template belongs to.  So the directive in the template will handle the ng-transclude 
                    //applied to the div as the transclude for the recursive compile call to the tree 
                    //directive.  The recursion will end when the ng-repeat above has no children to 
                    //walkthrough.  In other words, when we hit a leaf.
                    '<tree family="child"><div ng-transclude></div></tree>' +
                '</li>' +
            '</ul>',
        compile: function(tElement, tAttr, transclude) {
            //We are removing the contents/innerHTML from the element we are going to be applying the 
            //directive to and saving it to adding it below to the $compile call as the template
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {

                if(!compiledContents) {
                    //Get the link function with the contents frome top level template with 
                    //the transclude
                    compiledContents = $compile(contents, transclude);
                }
                //Call the link function to link the given scope and
                //a Clone Attach Function, http://docs.angularjs.org/api/ng.$compile :
                // "Calling the linking function returns the element of the template. 
                //    It is either the original element passed in, 
                //    or the clone of the element if the cloneAttachFn is provided."
                compiledContents(scope, function(clone, scope) {
                        //Appending the cloned template to the instance element, "iElement", 
                        //on which the directive is to used.
                         iElement.append(clone); 
                });
            };
        }
    };
});

整个事情正在运作中:http://jsfiddle.net/DsvX6/7/


14
很好的例子,但我必须说,如果那是Angular最好的表现,那么这种实现简单递归结构的方式有点疯狂。 - acjay
在这个示例中,添加一个链接功能到指令会打断这个过程吗?我无法让它工作。 - perrohunter
如果我修改上面的jsfiddle以延迟设置作用域变量(模拟从$http.get()调用获取数据),它不会捕获更改。您将如何修改实现以在数据更新时正确重建树? - Jim B.
强烈建议不要使用递归的Angular指令,因为这将成为一个巨大的性能问题。在每个递归调用中,DOM自动会变得更深,并且当涉及性能时,呈现深层DOM将变得非常昂贵。 - Sameera Manorathna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接