理解AngularJS指令 -- ng-repeat和compile
发布在用Angular开发web应用2014年2月28日view:28905
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

enter image description here

Miško Hevery,也就是AngularJS的作者,曾经在一个演讲中谈到指令,他说在用户自定义指令时,compile几乎不会被用到,它仅仅在相识ng-repeat和ng-view这样的指令中才会用到。因此,我们现在的问题是:ng-repeat是如何运行的?

在继续深入之前,我们先来复习一些transclusion这个可选项。Transclude有两个可选项:

1.true
2.'element'

首先我们来看看translude: true:

<div person>Ted</div>

app.directive('person',function(){
    return {
        transclude: true,
        template: "<h1>A person</h1><div ng-transclude></div>",
        link: function($scope,$element,$attr){
            //some code
        }
    }
}); 

指令运行的结果如下:

<h1>A Person</h1><div ng-transclude><span class='ng-scope'>Ted</span></div>

在上面的例子中,tranclude: true告诉Angular来接受DOM元素的内容,使用这个指令,并将它们插入到person的template中。为了指明模板的那一部分将用来插入DOM内容,我们需要在模板汇总包含ng-transclude指令。而span标签,连同ng-scope类则是由AngularJS自动插入的。

和上面的例子相比,在ng-repeat指令中,并不包含一个模板用于插入DOM内容。因此,ng-repeat将会设置transclude: ‘element’,来表明调用ng-repeat的DOM元素将会用于transclusion。

在下面,我们会自己实现一个与ng-repeat功能很相似的指令叫做lk-repeat,它运行的结果和用法同ng-repeat完全一样。下面是HTML代码部分:

<ul>
    <li lk-repeat="name in names">{{name}}</li>  
</ul>   

下面是指令定义的部分:

var app = angular.module('myApp',[]);

app.directive('lkRepeat',function(){
    return {
        transclude: 'element',
        compile: function(element,attar,linker){
            return function($scope,$element,$attr){
              var myLoop = $attr.lkRepeat,
                    match = myLoop.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
                    indexString = match[1],
                    collectionString = match[2],
                    parent = $element.parent(),
                    elements = [];

              //每次collection被修改时$watchCllection都会被调用  
              $scope.$watchCollection(collectionString,function(collection){
                var i,block,childScope;

                //检查元素是否已经被渲染了   
                if(elements.length > 0){
                //如果为真将它们从DOM中移除,并销毁它们的scope  
                for(i=0;i<elements.length;i++){
                    elements[i].el.remove();
                    elements[i].scope.$destory();
                };
                elements = [];
                }

                for(i=0;i<collection.length;i++){
                  //对这个collection中每个元素创建一个新scope  
                  childScope = $scope.$new();
                  childScope[indexString] = collection[i];

                  linker(childScope,function(clone){

                    parent.append(clone);//添加到DOM中  
                    block = {};
                    block.el = clone;
                    block.scope = childScope;
                    elements.push(block);
                  });
                }
              });
            }
        }
    }
});    

每一次当collection发生更新时,你注意到了所有的元素以及它们的scope都从DOM中被移除了。虽然这对于阅读代码来说很有好处,但是这样每次全部移除由重新添加的做法是非常没有效率的。在真实版本的ng-repeat中,只有从collection被移除的元素才会从DOM中被移除。另外,如果有一个项目在集合中改变了位置(比如从第二变为了第四),它并不需要一个新的scope,但是它需要在DOM中发生移动。


本文参考自Understanding AngularJS Directives Part 1: Ng-repeat and Compile,原文地址http://liamkaufman.com/blog/2013/05/13/understanding-angularjs-directives-part1-ng-repeat-and-compile/

如果你觉得本文对你有帮助,请点击为我提供赞助

评论
发表评论
4年前

$destory();拼错了,应该是destroy…%>_<%…这个错害得我研究了好几个小时…

WRITTEN BY
张小俊128
Intern in Baidu mobile search department。认真工作,努力钻研,期待未来更多可能。
TA的新浪微博
PUBLISHED IN
用Angular开发web应用

讲述Angular开发框架的基础知识,帮助读者快速学习并深入理解Angular的开发理念和具体应用方式

我的收藏