AngularJS:手动$compile与自然$compile在递归指令上的区别

6
我尝试使用AngularJS创建自己的递归指令,该指令会调用自身以便将对象转换为具有漂亮JSON格式的视图。首先,我使用ng-include调用带有模板的脚本,并在其中使用ng-if来验证当前值是否为对象,如果是,则模板会调用自身。
我一直认为这是一种丑陋的方法,所以我将其转换为了指令,更加简单。
更多或更少...因为我发现了递归指令的世界,并找到了许多有趣的东西。我甚至阅读了Angular在github中的源代码(我建议您阅读:https://github.com/angular/angular.js),这是一件好事。
我搜索了很久,我认为我快要找到能够满足我的答案了!因为我学到了很多新东西,而你们也会帮助我。
请查看下面链接中的代码:https://github.com/Daymannovaes/htmljs/blob/master/js/directives/recursiveDataTemplateDirective.js 我的指令是:recursive-data-template="data",其中数据是一个对象。此指令将循环遍历此对象的键和值,并且如果该值是对象,则会再次执行此操作。条件是使用ng-if="isObject(value)"创建的。
好的,我的第一个问题是无限循环。我需要在编译阶段删除内容,然后在postLink阶段以命令方式编译内容。我的问题是: **为什么手动编译不会遇到无限循环的相同问题?**
我正在编译相同的内容,没有进行任何条件(如果删除if(!compiledContent),则仍不会发生无限循环),区别(我认为)仅在于它们是在不同的位置制作的,但我无法在互联网上找到回答我的问题的人!
所以,谢谢! (如果链接不起作用,这里是重要代码):
compile: function(templateElement, templateAttributes) {
            /*
              in compile time, we need to remove the innerHTML of template(url) because of its recursive.
              Because of its recusiveness, when the $compile start to read the DOM tree and find a
              recursiveDataTemplate directive (even its not will be evaluated all time by link function
              because the ng-if, whatever) its start the do all the process again. So, we need the .remove()
             */
            var templateDirectiveContent = templateElement.contents().remove();
            var compiledContent;

            return function($scope, linkElement, linkAttributes) {

                /* 
                  This verification avoid to compile the content to all siblings, because
                  when you compile the siblings, don't work (I don't know why, yet).
                  So, doing this we get only the top level link function (from each iteration)
                 */
                if(!compiledContent) {
                    compiledContent = $compile(templateDirectiveContent);
                }

                /*
                  Calling the link function passing the actual scope we get a clone object
                  wich contains the finish viewed object, the view itself, the DOM!!
                  Then, we attach the new dom in the element wich contains the directive
                 */
                compiledContent($scope, function(clone) {
                  linkElement.append(clone); 
                });
            };
        },
  }
<ul>
    <li data-ng-repeat="(key, value) in fields">
        <span data-ng-if="!isNumber(key)">
            {{key}}:
        </span>

        <span data-ng-if="isObject(value)" recursive-data-template="value"></span>

        <span data-ng-if="!isObject(value)">
            {{value}}
        </span>

    </li>
</ul>

请查看:https://github.com/dotJEM/angular-tree,我个人认为你最初的递归ng-include的想法并不是一个坏主意,请记住,您可以将该想法推广到一组指令,这正是dotjem-angular-tree所做的。我认为拥有一对指令是合理的,一个定义起点,然后一个递归连接,因为这将允许您在树中进行树,而不会相互冲突。http://plnkr.co/edit/uaZVzUhPsDIsx93kzXSP?p=preview - Jens
1
这是一个非常好的问题,我很遗憾没有提供一个好的答案。 - erilem
我曾经遇到过同样的问题,并使用“compiledContent”提示解决了它。但我并不真正理解为什么会这样,我希望能得到一些关于所有这些东西如何工作的官方准确解释。 - floribon
2个回答

1

我认为这段来自官方文档的摘录与您所询问的相关:

注意:编译函数无法处理在其自己的模板或编译函数中递归使用自身指令。 编译这些指令会导致无限循环和堆栈溢出错误。 可以通过在后链接数功能中手动使用$compile来命令式编译指令的模板,而不是依靠通过templatetemplateUrl声明进行自动模板编译或在编译函数内部进行手动编译。

然后根据您提供的代码,您似乎已经按照此提示所建议的方式操作-即在您为指令的compile属性返回的函数(postLink)中手动编译。


好的,我已经读了它。但是我的问题是,为什么在postLink中编译不会触发无限循环呢?我正在编译整个templateUrl,其中包含它本身。 - daymannovaes
有趣的是,官方文档中有一条关于在post link函数中使用$compile的注释,但这并没有回答daymannovaes实际提出的问题。 - erilem

0
关于为什么在postLink阶段编译而不是compile阶段避免了无限递归的原因,那是因为DOM的所有元素都会被编译,无论它们是否实际使用,而link只有在元素实际链接时才会触发:例如,如果更高级别的ng-if为假,则其子元素将不会被预链接,因此也不会被后链接...至少从我的理解来看!
我推荐这篇好文章:http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives/

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