AngularJS源码学习一 :directive
标题党了。其实这篇文章是为了记录一个我对AngularJS compile过程的一个问题。
基础:
directive有个重要特性是transclude。这个特性主要用途是:当页面的dom元素要进行复杂变化时,将原本的dom暴露给用户以供变形时或变形后使用。例如我有个选项卡directive,写法是:
<div tabs> <div tab title="title1">content1</div> <div tab title="title2">content2</div> </div>
这个directive在执行时肯定要变换,变换结果是:
<div <div class="tab-titles"> <span class="tab-title">title1</span> <span class="tab-title">title2</span> </div> <div class="tab-contents"> <div class="tab-content">content1</div> <div class="tab-content">content2</div> </div> </div>
里面还有一些用来表示title和content对应的属性我省略了。
在变换的时候,可以看到原来的dom结构和变化后的content部分几乎是一样。因此可以直接transclude过来重用。其他例子可以看AngularJS官方文档的指南,也可以看ngRepeat源码。
这里要先提一下的概念是,AngularJS会给transclude的元素自动绑定一个新的scope。这个scope不是当前directive scope的child,而是sibling。也就以为它直接继承了当前这个directive的父scope的所有属性。所以当一个direcitve有transclude属性时,它会创建两个scope。第一个scope取决于用户对于scope属性的定义。第二个scope就是直接继承自父scope。
问题:
我遇到的问题是,当我试图调用transclude元素子元素(例如content1的div)的remove方法时,它会引起整个transclude的scope(上文中提到的第二个scope)都销毁。我翻了AngularJS源码。在compile.js文件419行找到了答案,答案是源码中在编译时给transclude元素绑定相应的scope时,还注册一个$destroy事件,当事件触发时就调用相应scope的$destroy()。而在AngularJS的jqLite中,当调用元素的remove方法时会主动触发$destroy事件。整个事件的触发也就清楚了。
不懂的是,没有transclude的directive所在的元素,即使指定了scope,绑定了新的scope,移除这个元素也不会造成相应scope被销毁。那为什么要给transclude的元素绑定了$destroy,使得元素被remove就销毁相应scope?
其他:
这里有两个点要提一下:
第一是directive的编译分为两个阶段,第一个阶段是compile,第二个是link。也就是先对元素做外所有变形,然后在将scope绑定上去,根据scope翻译其中的表达式。作者说这样做是为了提升性能,我的理解是这能尽快让页面显示出正确的dom结构,不知是否正确。
第二是,对于整个compile的过程,direcitve实际上相当于对开发者抛出的一个接口,通过这个接口,开发者就能全面地控制住视图的表现和视图与数据间的关系。这个接口设计得非常全面和合理,以至于我现在暂时想不到有什么控件用它开发不了,或者用它写不能节约大量代码。它的合理主要表现在对视图和数据这个双向连接流程的精准控制上。
欢迎任何批评和指点。