在编写Angular指令时,你可以使用以下任意函数来操作声明指令的元素的DOM行为、内容和外观:

编译 控制器 pre-link post-link

对于应该使用哪个函数,似乎有些困惑。这个问题包括:

基本指令

如何声明各种函数? 源模板和实例模板之间的区别是什么? 指令函数的执行顺序是什么? 在这些函数调用之间还会发生什么?

功能性质,该做的和不该做的

编译 控制器 Pre-link Post-link

相关问题:

指令:链接vs编译vs控制器。 在定义angular.js指令时,'controller', 'link'和'compile'函数之间的区别。 angularjs中的compile函数和link函数有什么区别? AngularJS指令中预编译元素和后编译元素的区别? Angular JS指令-模板、编译还是链接? Angular js指令中的post link vs pre link。


当前回答

Pre-link函数

每当实例化一个新的相关元素时,都会调用每个指令的预链接函数。

正如前面在编译顺序一节中看到的,预链接函数称为父-子函数,而后链接函数称为子-父函数。

预链接功能很少使用,但在特殊情况下可能有用;例如,当一个子控制器向父控制器注册自己时,但是注册必须以父-然后子的方式进行(ngModelController就是这样做的)。

不:

检查子元素(它们可能还没有呈现,绑定到范围等)。

其他回答

Pre-link函数

每当实例化一个新的相关元素时,都会调用每个指令的预链接函数。

正如前面在编译顺序一节中看到的,预链接函数称为父-子函数,而后链接函数称为子-父函数。

预链接功能很少使用,但在特殊情况下可能有用;例如,当一个子控制器向父控制器注册自己时,但是注册必须以父-然后子的方式进行(ngModelController就是这样做的)。

不:

检查子元素(它们可能还没有呈现,绑定到范围等)。

控制器的功能

每当一个新的相关元素被实例化时,就会调用每个指令的控制器函数。

正式来说,控制器函数是这样的:

定义可以在控制器之间共享的控制器逻辑(方法)。 启动作用域变量。

同样,重要的是要记住,如果指令涉及一个孤立的作用域,那么其中继承自父作用域的任何属性都还不可用。

Do:

定义控制器逻辑 初始化范围变量

不:

检查子元素(它们可能还没有呈现,绑定到范围等)。

在这些函数调用之间还会发生什么?

不同的指令函数是在另外两个angular函数$compile(指令的编译函数在这里执行)和一个内部函数nodeLinkFn(指令的控制器、preLink和postLink在这里执行)中执行的。在调用指令函数之前和之后,angular函数中会发生各种事情。也许最值得注意的是子递归。下面的简化图显示了编译和链接阶段的关键步骤:

为了演示这些步骤,让我们使用下面的HTML标记:

<div ng-repeat="i in [0,1,2]">
    <my-element>
        <div>Inner content</div>
    </my-element>
</div>

使用以下指令:

myApp.directive( 'myElement', function() {
    return {
        restrict:   'EA',
        transclude: true,
        template:   '<div>{{label}}<div ng-transclude></div></div>'
    }
});

编译

编译API看起来像这样:

compile: function compile( tElement, tAttributes ) { ... }

参数通常以t作为前缀,表示所提供的元素和属性是源模板的元素和属性,而不是实例的元素和属性。

在删除编译被传输内容(如果有的话)的调用,并将模板应用于标记之前。因此,提供给compile函数的元素将如下所示:

<my-element>
    <div>
        "{{label}}"
        <div ng-transclude></div>
    </div>
</my-element>

请注意,此时未重新插入所传输的内容。

在调用指令的.compile之后,Angular将遍历所有子元素,包括那些指令刚刚引入的元素(例如模板元素)。

实例创建

在我们的例子中,将创建上述源模板的三个实例(通过ng-repeat)。因此,下面的序列将执行三次,每个实例一次。

控制器

控制器API包括:

controller: function( $scope, $element, $attrs, $transclude ) { ... }

进入链接阶段,通过$compile返回的链接函数现在具有作用域。

首先,如果被请求,link函数创建一个子作用域(scope: true)或一个隔离作用域(scope:{…})。

然后执行控制器,并提供实例元素的作用域。

预链接

预链接API如下所示:

function preLink( scope, element, attributes, controller ) { ... }

实际上,在调用指令的.controller和. prelink函数之间没有发生任何事情。Angular仍然提供了如何使用它们的建议。

在. prelink调用之后,link函数将遍历每个子元素——调用正确的link函数并将当前作用域(用作子元素的父作用域)附加到它。

Post-link

post-link API类似于pre-link函数:

function postLink( scope, element, attributes, controller ) { ... }

也许值得注意的是,一旦一个指令的. postlink函数被调用,它所有子元素的链接过程就完成了,包括所有子元素的. postlink函数。

这意味着在调用. postlink时,子进程已经“活”了。这包括:

数据绑定 transclusion应用 范围在

这个阶段的模板看起来是这样的:

<my-element>
    <div class="ng-binding">
        "{{label}}"
        <div ng-transclude>                
            <div class="ng-scope">Inner content</div>
        </div>
    </div>
</my-element>

Post-link函数

当post-link函数被调用时,之前的所有步骤都已经发生了——绑定、传输等。

这通常是进一步操作呈现的DOM的地方。

Do:

操作DOM(呈现,并因此实例化)元素。 附加事件处理程序。 检查子元素。 建立对属性的观察。 在瞄准镜上设置监视。

编译函数

当Angular启动时,每个指令的compile函数只被调用一次。

正式来说,这是执行不涉及范围或数据绑定的(源)模板操作的地方。

这样做主要是为了优化;考虑下面的标记:

<tr ng-repeat="raw in raws">
    <my-raw></my-raw>
</tr>

<my-raw>指令将呈现一组特定的DOM标记。所以我们可以:

允许ng-repeat复制源模板(<my-raw>),然后修改每个实例模板的标记(在compile函数之外)。 修改源模板以包含所需的标记(在compile函数中),然后允许ng-repeat复制它。

如果raws集合中有1000个项目,后一种选择可能比前一种更快。

Do:

操作标记,使其充当实例(克隆)的模板。

附加事件处理程序。 检查子元素。 建立对属性的观察。 在瞄准镜上设置监视。