原文:http://docs.ngnice.com/api/ng/service/$compile

写在前面的话:

之前我一直理解错误,我一直以为这句--function([scope], cloneLinkingFn)— 是在声明一个函数。其实不是,这是一个函数的调用表达式,函数名字是function(真正在代码中是使用传入的参数名)。在下面的文章里会看到transcludeFn、$transclude,还有compile属性中的transclude,这三个参数其实都是一个东西,只是在不同的阶段叫不同的名字,使用这三个参数时都是这么使用的:function([scope], cloneLinkingFn);---在AngularJS中这就是一个link function的标准形式,所以说上面的三个参数都是link function。这里的link function是一个大概念,不是特指自定义指令是的link配置属性的link function。

AngularJS版本:1.2.18

本文根据上面链接中的文档翻译而成,加入了自己的一些理解。总的来看,本文详细介绍了AngularJS中指令的各个配置参数,基本不涉及原理性的东西。下面正文开始。

把一个HTML字符串或者DOM编译成为一个template(模板)并产生一个模板函数(template function),这个函数可以被用来将scope(作用域)和模板(template)链接(link)起来。

编译是一个通过爬DOM树把DOM元素与directive(指令)相匹配的过程。

理解directive API

对于一个指令来说,它有许多不同的配置项。

创建指令时,工厂函数(factory function)的返回值存在差异。你可以返回一个“Directive Definition Object”(指令的配置对象),也可以只返回一个postLink函数(此时指令的其它配置项均为默认值)。

下面是用Directive Definition Object声明一个指令的示例代码:

  1. var myModule = angular.module(...);
  2.  
  3. myModule.directive('directiveName', function factory(injectables) {
  4. var directiveDefinitionObject = {
  5. priority: 0,
  6. template: '<div></div>', // or // function(tElement, tAttrs) { ... },
  7. // or
  8. // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
  9. transclude: false,
  10. restrict: 'A',
  11. scope: false,
  12. controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
  13. controllerAs: 'stringAlias',
  14. require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
  15. compile: function compile(tElement, tAttrs, transclude) {
  16. return {
  17. pre: function preLink(scope, iElement, iAttrs, controller) { ... },
  18. post: function postLink(scope, iElement, iAttrs, controller) { ... }
  19. }
  20. // or
  21. // return function postLink( ... ) { ... }
  22. },
  23. // or
  24. // link: {
  25. // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
  26. // post: function postLink(scope, iElement, iAttrs, controller) { ... }
  27. // }
  28. // or
  29. // link: function postLink( ... ) { ... }
  30. };
  31. return directiveDefinitionObject;
  32. });

注意:任何未指定的配置项将会使用默认值。

因此,上面的代码可以简化为:

  1. var myModule = angular.module(...);
  2.  
  3. myModule.directive('directiveName', function factory(injectables) {
  4. var directiveDefinitionObject = {
  5. link: function postLink(scope, iElement, iAttrs) { ... }
  6. };
  7. return directiveDefinitionObject;
  8. // or
  9. // return function postLink(scope, iElement, iAttrs) { ... }
  10. });

Directive Definition Object

priority属性

当一个DOM元素上有多个指令的时候,有时需要指定这些指令的执行顺序。在指令自身的compile函数被调用前,指令会被按照其priority属性的大小进行排序。Priority属性的值是一个数字。

指令的priority属性的值越大,将会越早被编译。Pre-link函数(pre-link function)也会按照priority属性确定的顺序执行,但是post-link函数则会倒序执行。如果有指令的priority属性的值相同,那么他们的顺序没有指定谁先谁后。

Priority属性的值默认为0.

terminal属性

如果指令的terminal属性的值被设为了true,那么不管这个指令的priority属性值为多少,这个指令的顺序将会被排到其所在指令集的最后。

scope属性

(默认情况下,scope属性的值为false,指令不会创建作用域,而是直接使用父作用域。)

如果scope的值设为true,那么将会为这个指令创建一个新的作用域(scope)。如果在同一个元素上有多个指令需要一个新的作用域,那么将会只有一个作用域会被创建。这个新作用域的规则不适用于模板的根,因为模板的根总会获得一个新的作用域。

例子:

  1. scopeApp.directive('exrrScope',function() {
  2. return {
  3. restrict:'AE',
  4. scope:true,
  5. controller:function($scope, $element, $attrs, $transclude) {
  6. $scope.name = 'fuck you man!';
  7. }
  8.  
  9. };
  10. });
  11. scopeApp.directive('exttScope',function() {
  12. return {
  13. restrict:'AE',
  14. scope:true,
  15. template:'<h1>scope属性为true,变量name的值:{{name}}<input type="text" ng-model="name" /></h1>'
  16. };
  17. });
  1. <div exrr-scope extt-scope></div>

运行结果:

第一个指令和第二个指令共用一个作用域。

如果scope的值为 {}(object hash---对象哈希/散列),那么指令将会创建一个新的孤立作用域(isolate scope)。孤立作用域不同于常规的作用域(normal scope),也就是说孤立作用域不会原型继承父作用域。当我们创建可重用的组件时,孤立作用域是非常有用的,这可以防止指令意外地读取或者修改父作用域中的数据。

孤立作用域通过一个对象散列定义了一组源自父作用域的本地作用域属性(local scope properties)。这些本地属性对于模板中的变量取值非常有用。

本地作用域(孤立作用域)的本地变量是一个对象散列,源自于:

1、@或者@attr   把一个本地作用域属性与一个DOM属性的值绑定。因为DOM属性的值通常是一个字符串,所以结果通常是绑定一个字符串(言外之意就是@绑定只能绑定字符串值)。如果DOM属性的名字与本地作用域属性的名字相同,那么可以不用添加attr字段。比如:

  1. <widget my-attr="hello {{name}}">

widget 指令的作用域散列:

  1. scope: {
  2. localName:'@myAttr'
  3. }

那么widget指令的作用域属性localName的值就等于hello{{name}}。Widget指令作用域上的localName属性的值会随着name的值改变而改变。name的值从父作用域中读取。(@绑定是单向的,在父作用域中修改属性值可能会影响到孤立作用域中的值;而在孤立作用域修改属性的值一定不会影响到父作用域)。

例子:

  1. scopeApp.controller('threeCtrl',function($scope) {
  2. $scope.blink = 'blink';
  3. });
  4. scopeApp.directive('ex4Scope',function() {
  5. return {
  6. restrict:'AE',
  7. scope:{
  8. bb:'@'
  9. },
  10. template:'bb:<input type="text" ng-model="bb" /><h2>bb的值:{{bb}}</h2>'
  11. };
  12. });
  1. <!-- @或者@attr的绑定 -->
  2. <div ng-controller="threeCtrl">
  3. <h3>@绑定</h3>
  4. <h4>blink的值:<input type="text" ng-model="blink" /></h4>
  5. <!--这种写法在任一作用域中修改属性值均不会影响到另一作用域-->
  6. <ex4-scope bb="blink"></ex4-scope>
  7. <!--这种写法在父作用域中修改属性值会影响到孤立作用域,在孤立作用域中修改不会影响到父作用域-->
  8. <ex4-scope bb="{{blink}}"></ex4-scope>
  9. </div>

2、=或者=attr   将一个本地作用域属性与通过attr字段指定的DOM属性(DOM属性的值等于父作用域的属性名)进行双向绑定(本质上是与父作用域的属性进行双向绑定)。如果DOM属性的名字与本地作用域属性的名字相同,那么可以不用添加attr字段。比如:

  1. <widget my-attr="parentModel">

widget 指令的作用域散列:

  1. scope: {
  2. localModel:'=myAttr'
  3. }

在widget指令的作用域中,属性localModel的值就等于父作用域中parentModel属性的值。parentModel值的改变将引起localModel值的改变。同样的,localModel值的改变也会引起parentModel值的改变。如果父作用域中不存在parentModel属性,将会抛出NON_ASSIGNABLE_MODEL_EXPRESSION异常。可以使用=?或者=?attr将属性标记为可选的,来避免抛出异常。

例子:

  1. scopeApp.controller('twoCtrl',function($scope) {
  2. $scope.two = {
  3. name:'two',
  4. age:'18'
  5. };
  6. $scope.blink = 'blink';
  7. });
  8. scopeApp.directive('ex3Scope',function() {
  9. return {
  10. restrict:'AE',
  11. scope:{
  12. test:'=bb',
  13. xx:'='
  14. },
  15. template:'<h1>scope =绑定,变量name的值:{{test.name}},第二个绑定:{{xx}}</h1><input type="text" ng-model="xx" />'
  16. };
  17. });
  1. <div ng-controller="twoCtrl">
  2. <h4>{{blink}}</h4>
  3. <ex3-scope bb="two"></ex3-scope>
  4. <ex3-scope bb="two" xx="blink"></ex3-scope>
  5. </div>

3、&或者&attr   提供一种在父作用域的上下文中执行一个表达式的途径。如果DOM属性的名字与本地作用域属性的名字相同,那么可以不用添加attr字段。比如:

  1. <widget my-attr="count = count + value">

Widget指令的作用域散列:

  1. scope: {
  2. localFn:'&myAttr'
  3. }

那么,孤立作用域(本地作用域)属性localFn将会指向一个包装了表达式count = count + value的函数包装器(其实是将表达式用一个匿名函数包装,这个匿名函数可以通过作用域链(原生js中的概念)查询到父作用域的变量值)。

通常通过表达式从孤立作用域向父作用域传递数据被认为是必要的,这可以通过向表达式包装函数传入键值对实现。比如,如果表达式是increment(amount),那么我们可以这样调用localFn并同时指定amount的值:localFn({amount: 22})。

例子:

  1. scopeApp.controller('fourCtrl',function($scope) {
  2. $scope.name = 'four';
  3. $scope.test = function(ss) {
  4. alert(ss);
  5. console.log(ss);
  6. };
  7. });
  8. scopeApp.directive('ex5Scope',function() {
  9. return {
  10. restrict:'AE',
  11. scope:{
  12. func:'&',
  13. getname:'&'
  14. },
  15. template:'<input type="text" ng-model="ff" /></br>' +
  16. '{{ff}}' +
  17. '<button type="button" value="click" ng-click="func({ss:ff})">点点</button>' +
  18. '<p>{{uu}}</p>',
  19. controller: function($scope) {
  20. console.log($scope);
  21. $scope.uu = $scope.getname();
  22. }
  23. };
  24. });
  1. <div ng-controller="fourCtrl">
  2. <h3>&绑定</h3>
  3. <ex5-scope func="test(ss)"></ex5-scope>
    <!--通过指令元素的getname属性以及&绑定,父作用域的name属性经过匿名函数包装后绑定给了孤立作用域的getname属性(此时孤立作用域的getname属性指向这个函数),在指令的控制器中可以通过调用这个函数获得父作用域中的name的值-->
  4. <ex5-scope getname="name"></ex5-scope>
  5. </div>

controller属性

值是一个控制器构造函数。控制器的实例化是在预链接(pre-linking)阶段之前(在 compile 函数之后, link 函数之前被执行。),并且该控制器可以被其它指令共享(通过require属性实现),这可以实现指令之间的交互。

控制器中可以注入以下变量:

1、$scope – 与指令所在元素相关联的作用域;scope属性为false时,指向父作用域;scope属性不为false时,指向指令创建的作用域。

2、$element – 指令所在的元素

3、$attrs – 指令所在元素的属性对象

4、$transclude – 一个transclude链接函数,这个函数预绑定了正确的transclusion scope。这个预绑定的作用域可以通过函数的第一个参数重写。

transclude链接函数:function([scope], cloneLinkingFn)

require属性

依赖另一个指令并且注入所依赖指令的控制器作为链接函数(link function)的第四个参数。Require属性的值是所依赖指令的字符串名字。如果依赖多个指令,那么require属性的值是由这些指令的名字所组成的数组,对应的链接函数的第四个参数也是一个数组,数组的项是对应的控制器实例。如果不存在所依赖的指令或者所依赖的指令没有控制器,那么会产生一个错误。

所依赖的指令的名字前可以添加如下前缀:

  1. 不加前缀 – 在指令所在的元素上查找所依赖的指令的控制器,如果没有寻找到就抛出错误;
  2. ? – 尝试在指令所在的元素上查找所依赖的指令的控制器,如果没有找到就将null作为第四个参数传入链接函数(link function);
  3. ^ -- 在指令所在元素或者指令所在元素的父元素上查找所依赖的指令的控制器,如果没有寻找到就抛出错误;
  4. ?^ --在指令所在元素或者指令所在元素的父元素上查找所依赖的指令的控制器,如果没有找到不抛出错误,将null作为第四个参数传入链接函数(link function);

例子:

  1. scopeApp.directive('ex4Require',function() {
  2. return {
  3. restrict:'AE',
  4. //template:'',
  5. controller: function($scope,$element,$attrs) {
  6. $scope.name = 'four';
  7. this.four = function() {
  8. console.log($scope.name);
  9. };
  10. }
  11. };
  12. });
  13. scopeApp.directive('ex5Require',function() {
  14. return {
  15. restrict:'AE',
  16. template:'<h3 ng-click="four()">第⑤个指令</h3>',
  17. require:'^ex4Require',
  18. controller: function($scope,$element,$attrs) {
  19. $scope.name = 'five';
  20. this.five = function() {
  21. console.log(5);
  22. };
  23. },
  24. link: function(scope, elem, attrs, ctrl) {
  25. scope.four = ctrl.four;
  26. }
  27. };
  28. });
  1. <!--下面两种情况,点击第⑤个指令,控制台都会输出five-->
  2. <ex4-require>
  3. <div ex5-require></div>
  4. </ex4-require>
  5. <div ex4-require ex5-require></div>

controllerAs属性

指定控制器在指令作用域中的别名。通过这个别名可以在指令的模板中引用这个控制器。实质上是在指令的作用域上添加一个属性,属性的名字是controllerAs的值,这个属性的值指向这个控制器的一个实例。

例子:

  1. scopeApp.directive('ex1Controlleras',function() {
  2. return {
  3. restrict:'AE',
  4. scope:true,
  5. transclude:true,
  6. controller: function($scope,$element,$attrs) {
  7. $scope.name = 'one';
  8. console.log($scope);
  9. this.one = function() {
  10. console.log(1);
  11. };
  12. },
  13. controllerAs:'test',
  14. template:'<h3 ng-click="test.one()">第一个指令</h3>',
  15. };
  16. });
  1. <ex1-controlleras></ex1-controlleras>

restrict属性

Restrict属性的值是字符串‘ECMA’的一个子集,约束了指令的调用风格。如果省略该字段,则默认值为‘A’。

  • E – 元素:

    1. <!--E -- 元素-->
    2. <my-directive></my-directive>
  • A – 属性: 
    1. <!--A – 属性 -->
    2. <div my-directive="exp"></div>
  • C – 类: 
    1. <!--C – 类 -->
    2. <div class="my-directive: exp;"></div>
  • M – 注释: 
    1. <!-- M – 注释 -->
    2. <!-- directive: my-directive exp -->

template属性

该属性指定了指令的模板,用模板填充指令所在元素的内容。

template的值可以是一个HTML字符串,也可以是一个函数。这个函数接收两个参数tElement 和 tAttrs(关于这两个参数在compile属性中介绍),并且返回一个HTML字符串。

templateUrl属性

该属性制定一个加载模板的URL。由于模板的加载是异步的,所以模板的编译和链接(compilation/linking)是在模板加载完成之后才进行的。

templateUrl的值可以是一个URL字符串,也可以是一个函数,这个函数有两个参数tElement 和 tAttrs(关于这两个参数在compile属性中介绍),并且返回一个URL的字符串。

replace 属性(这个属性将在下个主要版本中移除)

这个属性指定模板的插入位置。默认值为false。

true – 用template/templateUrl属性指定的模板替换指令所在的元素,此时模板的HTML片段只能有一个根元素,否则将报错。

false – 指令所在元素的HTML内容将被替换为模板(相当于重置指令所在元素的innerHTML属性)。

例子:

  1. replaceApp.directive('exOneReplace',function() {
  2. return {
  3. restrict:'AE',
  4. replace:true,//默认为false
  5. template:'<h1>我轻轻走过你身旁</h1>'
  6. };
  7. });
  8. replaceApp.directive('exTwoReplace',function() {
  9. return {
  10. restrict:'AE',
  11. //默认为false,
  12. //如果改为true,则控制台报错:
  13. //angular.js:14423 Error: [$compile:tplrt] Template for directive 'exTwoReplace' must have exactly one root element.
  14. replace:false,
  15. template:'<h1>并没有话对你讲</h1><p>test</p>'
  16. };
  17. });
  1. <!--指令所在元素的HTML属性都转移到了<h1>元素上了-->
  2. <ex-one-replace id="one"></ex-one-replace>
  3. <div id="three" ex-one-replace></div>
  4.  
  5. <ex-two-replace id="two"></ex-two-replace>
  6. <div id="four" ex-two-replace></div>

transclude属性

Transclude属性的默认值为flase。当值不为false时,编译指令所在元素的原有内容以便能让指令利用(一般要和ngTransclude指令配合使用),此时链接函数(linking function)将会收到一个transclusion函数(transclusion function,已经预绑定了相应的作用域)作为参数(至于是第几个参数参考compile属性和link属性)。如果指令创建了一个isolate作用域,那么transclusion作用域与isolate作用域的关系是兄弟关系,二者互不影响—待验证(验证与isolate作用域,与父作用域的关系)。

举例说明下面两个值的差异

true  -- transclude the content of the directive

‘element’ – transclude the whole element including any directives defined at lower priority

例子:

  1. transcludeApp.controller('transCtrl', ['$scope', '$window', function($scope,$window) {
  2. $scope.name = "trans controller";
  3. $scope.age = "22";
  4. }]);
  5. transcludeApp.directive('transScope', function() {
  6. return {
  7. restrict: 'EA',
  8. template: '<h3>这是指令中的内容</h3><div ng-transclude=""></div>',
  9. scope:{},//创建isolate scope
  10. transclude: true,
  11. compile: function(elem, attrs, trans) {
  12. console.log(trans);
  13. return function (scope, element, attrs) {
  14. scope.name = "directive scope";
  15. trans(scope, function(clone) {
  16. //这里的scope是transclude scope
  17. //当transclude的值为true时,这里的clone是指令所在元素所包含的所有子节点,包括文本节点(不包含指令template/templateUrl属性指定的HTML)
  18. //当transclude的值为'element'时,这里的clone是指令所在元素(此时指令好像没有执行一样,不知道为什么)
  19. console.log(clone);
  20. console.log(clone[0]);
  21. console.log(clone.context);
  22. console.log(scope.$parent);
  23. });
  24. console.log(scope);//这个scope是指令创建的isolate scope
  25. };
  26. }
  27. };
  28. });
  1. <div ng-controller="transCtrl">
  2. <h2>transCtrl的name:<input type="text" ng-model="name" /></h2>
  3. <h2>transCtrl的age:<input type="text" ng-model="age" /></h2>
  4. <div trans-scope>
  5. <h3>这不是指令中的内容,是transclude scope中的内容:</h3>
  6. <p>transCtrl中的name属性的值:{{name}}<input type="text" ng-model="name" /></p>
  7. <p>transCtrl中的age属性的值:{{age}}</p>
  8. </div>
  9. </div>

compile属性

  1. function compile(tElement, tAttrs, transclude) { ... }

编译函数(compile function)用来处理模板DOM的转换(transform)。因为大多数指令都不进行模板的转换,所以编译函数不常用到。编译函数接收以下参数:

tElement - 模板元素(template element) – 指令所在的元素,在该元素及其子元素上进行的转换是安全的。

tAttrs - 模板属性(template attributes)-- 指令所在元素的规范化的属性列表,在元素上的所有指令的编译函数都共享此属性列表。

transclude – [不推荐使用] 一个transclude linking function:function(scope, cloneLinkingFn)

注意:如果模板(这里指的是指令所在的元素,不要与template属性指定的HTML模板搞混了)被克隆了,那么模板实例与链接实例可能不是同一个对象。基于此原因,那么在编译函数中对所有克隆的DOM节点进行除DOM变换之外的操作都是不安全的。特别的,DOM事件的监听注册应该在链接函数(link function)中进行,而不是在编译函数(compile function)中。

注意:编译函数不能处理那些在自己的模板中调用自己的指令(递归地使用指令),编译这些递归使用的指令将会导致一个死循环和栈溢出错误。要避免上述情况的发生,可以在postLink函数(postLink function)中手动调用 $compile服务强制编译指令的模板,替换由指令的template或者templateUrl属性指定的模板或者在编译函数中手动编辑的模板。

注意:不推荐使用传入编译函数的transclude函数,如果要用transclude函数请在链接函数中使用。

编译函数可以返回一个函数或者一个对象:

当返回一个函数时,返回的是postLink函数,当不指定compile属性时(不指定编译函数),指定link属性(指定link function – 链接函数)就是在指定postLink函数(不能给一个指令同时即指定编译函数又指定链接函数,如果二者同时存在,编译函数会覆盖链接函数)。

当返回一个对象时,这个对象有两个属性pre 和 post ,分别对应preLink函数和postLink函数。关于这两个函数参考link属性中的相关内容。

link属性

这个属性只有在compile属性未定义的时候才使用(本质上是指定一个postLink函数)。

  1. function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }

这个链接函数(link function)负责注册DOM监听器(事件)以及更新DOM。这个函数在模板被克隆之后执行。链接函数里包含了当前指令的大部分逻辑。

链接函数的参数说明:

scope – 作用域 -- 指令利用这个scope注册监视器。

iElement – 实例元素 – 调用指令的元素。在postLink函数中操作实例元素的子元素是安全的,因为子元素已经被链接过了。

iAttrs – 实例属性 – 声明在实例元素上的所有属性(HTML属性)组成的规范化的属性列表,被该实例元素调用的所有指令的链接函数共享。

controller – 一个控制器实例 – 如果实例元素所调用的指令中至少有一个指令定义了一个控制器,那么这个控制器可以被实例元素所调用的所有指令共享,这可以允许不同指令之间通过控制器进行通信(控制器的共享需要require属性配合)。

transcludeFn --  一个预绑定了相应的transclusion scope的transclude linking function。transclusion scope可以被该函数的第一个参数(可选的)覆盖。要使用这个函数也可以在指令的控制器中注入$transclude服务(ME:$transclude本质上就是就是function([scope], cloneLinkingFn))实现,在这两个地方使用transclude linking function是完全等价的。

transclude linking function: function([scope], cloneLinkingFn)

Pre-linking function

在子元素被链接之前执行。不要在该函数里进行DOM的变换,因为编译器链接函数(compiler linking function)将不能找到正确的元素进行链接。

Post-linking function

在子元素被链接之后执行。在post-linking函数中进行DOM的变换是安全的。

Attributes

属性对象 – 作为参数传入link()或者compile()函数。这个对象有多种用途。

访问规范化的属性名:像‘ngBind’指令可以有多种表示方式:‘ng:bind’,‘data-ng-bind’,或者‘x-ng-bind’。属性对象允许通过标准的属性名访问属性。

属性对象的作用:

指令之间的通信:所有的指令共享同一个属性对象的实例,这可以让不同指令利用这个属性对象进行指令间的通信。

支持 interpolation (插值): interpolation attributes被分配到属性对象,这可以允许其他指令读取 interpolation value。

观察(监听)属性的值(interpolated attributes):利用$observe监听属性值的改变,包括插入的属性(插值属性)(例如:src="{{bar}}")。尽管这样很不高效,但是这是唯一简便的获取属性的实际值的方法。因为在链接阶段(linking phase)插值还没有被计算出来,并且此时这个值被设为undefined。

文档上的例子:

  1. function linkingFn(scope, elm, attrs, ctrl) {
  2. // get the attribute value
  3. console.log(attrs.ngModel);
  4.  
  5. // change the attribute
  6. attrs.$set('ngModel', 'new value');
  7.  
  8. // observe changes to interpolated attribute
  9. attrs.$observe('ngModel', function(value) {
  10. console.log('ngModel has changed value to ' + value);
  11. });
  12. }

下面是一个使用的例子。

备注:一般使用module.directive定义指令,下面的例子用来说明$compile是如何工作的。

文档中的例子:

  1. <script>
  2. angular.module('compile', [], function($compileProvider) {
  3. // configure new 'compile' directive by passing a directive
  4. // factory function. The factory function injects the '$compile'
  5. //传入一个指令的工厂函数组装一个新的‘compile’函数,这个工厂函数注入了‘$compile服务’
  6. $compileProvider.directive('compile', function($compile) {
  7. // directive factory creates a link function
  8. //指令工厂函数创建了一个link function
  9. return function(scope, element, attrs) {
  10. scope.$watch(
  11. function(scope) {
  12. // watch the 'compile' expression for changes
  13. //监听‘compile’表达式的变化
  14. return scope.$eval(attrs.compile);
  15. },
  16. function(value) {
  17. // when the 'compile' expression changes
  18. // assign it into the current DOM
  19. //当‘compile’表达式变化的时候,将它分配给当前DOM
  20. element.html(value);
  21.  
  22. // compile the new DOM and link it to the current
  23. // scope.
  24. // NOTE: we only compile .childNodes so that
  25. // we don't get into infinite loop compiling ourselves
  26. //编译这个新的DOM,并且将其链接到当前作用域
  27. //注意:只编译.childNodes,这样就不会因为编译到自己而进入无限循环。
  28. $compile(element.contents())(scope);
  29. }
  30. );
  31. };
  32. })
  33. });
  34.  
  35. function Ctrl($scope) {
  36. $scope.name = 'Angular';
  37. $scope.html = 'Hello {{name}}';
  38. }
  39. </script>
  40. <div ng-controller="Ctrl">
  41. <input ng-model="name"> <br>
  42. <textarea ng-model="html"></textarea> <br>
  43. <div compile="html"></div>
  44. </div>

$compile的用法

  1. $compile(element, transclude, maxPriority);

Tips:当编译器编译HTML元素时,如果编译过程中找到了指令,指令的 compile 函数会接受传入$compile的 transclude 函数作为其参数,也就是说前面的function([scope], cloneLinkingFn)就是下文的function(angular.scope, cloneAttachFn=),调用这个函数返回一个节点。

参数

参数

类型

详情

element

字符串或DOM元素

将元素或HTML字符串编译成一个模板函数

transclude

function(angular.scope, cloneAttachFn=)

可被指令利用的函数

maxPriority

数字

只应用于priority属性小于该值的指令(只影响根元素,不影响其子元素)

关于第三个参数maxPriority,我的理解是用于限定哪些指令需要编译。如果指令的priority属性的值小于maxPriority的值,那么就编译这个指令。

返回值

function(angular.scope, cloneAttachFn=)  一个用于将模板(一个DOM元素/树)绑定到作用域上的link function(链接函数)。

这个函数的参数说明:

scope – 一个要绑定的作用域。

cloneAttachFn – 如果提供了cloneAttachFn函数,那么这个link function将会克隆一份模板(template)并且调用cloneAttachFn函数,同时将克隆的元素添加到DOM文件的合适的地方。

cloneAttachFn函数以cloneAttachFn(clonedElement, scope)的形式调用,其中:

cloneElement – 是传入编译器的源element的一份克隆;

scope – 是link function所处的当前作用域。

调用这个链接函数将返回模板元素节点。如果没有提供cloneAttachFn函数,那么这个模板元素就是传入的源element;如果提供了cloneAttachFn函数,那么就是源element的一份克隆。

链接完成之后调用$digest之前,视图(view)是不会被更新的。一般情况下,Angular会自动调用$digest。

如果需要访问绑定视图(the bound view),有两种方法可以实现:

1、 如果不让链接函数(link function)克隆模板,那么在将它们(模板元素)发送到编译器之前创建DOM元素,并保留这个引用:

  1. var element = $compile('<p>{{total}}</p>')(scope);

2、如果需要克隆模板元素,那么视图的引用(view reference)不是指向这个克隆体,而是指向被克隆的源模板。在这种情况下,可以通过cloneAttachFn函数访问这个克隆体:

  1. var templateElement = angular.element('<p>{{total}}</p>'),
  2. scope = ....;
  3.  
  4. var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
  5. //attach the clone to DOM document at the right place
  6. });
  7. //now we have reference to the cloned DOM via `clonedElement`

想了解更多关于编译器是如何工作的的信息,请参考 Angular HTML Compiler

最后我想说的是,我还是不知道transcludeFn到底在什么情境下使用。

相关文章:

1、AngularJS Compile的细节

2、HTML Compile

3、angular中的transclude

AngularJS-----$compile的更多相关文章

  1. Angular源码分析之$compile

    @(Angular) $compile,在Angular中即"编译"服务,它涉及到Angular应用的"编译"和"链接"两个阶段,根据从DO ...

  2. Compile FreeCAD on Windows

    Compile FreeCAD on Windows eryar@163.com 1.Introduction FreeCAD是一个参数化的三维造型软件,主要用于任意大小的实际模型的设计.参数化的建模 ...

  3. maven 加入json-lib.jar 报错 Missing artifact net.sf.json-lib:json-lib:jar:2.4:compile

    <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</art ...

  4. $compile

    <html ng-app="compile"> <head> <script src="http://apps.bdimg.com/libs ...

  5. 指令<AngularJs>

    对于指令,可以把它简单的理解成在特定DOM元素上运行的函数,指令可以扩展这个元素的功能. 首先来看个完整的参数示例再来详细的介绍各个参数的作用及用法: angular.module('myApp', ...

  6. 关于The C compiler "arm-none-eabi-gcc" is not able to compile a simple test program. 的错误自省...

    在 GCC ARM Embedded https://launchpad.net/gcc-arm-embedded/ 上面下载了个arm-none-eabi-gcc 用cmake 编译时 #指定C交叉 ...

  7. My first makefile to compile multiple C files

    I have three files to compile: main.c, func.c,  func.h The steps: 1   main.c   to   main.o 2   func. ...

  8. angular中的compile和link函数

    angular中的compile和link函数 前言 这篇文章,我们将通过一个实例来了解 Angular 的 directives (指令)是如何处理的.Angular 是如何在 HTML 中找到这些 ...

  9. Angular使用$compile为从Ajax加载的HTML绑定ng-click事件

    这是一个Angular使用$compile为从Ajax加载的HTML绑定ng-click事件的实现方式,由于近期忙碌,就先放代码.代码如下: <table data-ng-table=" ...

  10. 我的angularjs源码学习之旅1——初识angularjs

    angular诞生有好几年光景了,有Google公司的支持版本更新还是比较快,从一开始就是一个热门技术,但是本人近期才开始接触到.只能感慨自己学习起点有点晚了.只能是加倍努力赶上技术前线. 因为有分析 ...

随机推荐

  1. C#中的DllImport使用方法

    DllImport是System.Runtime.InteropServices命名空间下的一个属性类,其功能是提供从非托管DLL导出的函数的必要调用信息 DllImport属性应用于方法,要求最少要 ...

  2. ovs-ofctl: s1 is not a bridge or a socket 解决方法

    参考: ovs-vsctl: Error detected while setting up bridge ovs-ofctl: s1 is not a bridge or a socket 解决方法 ...

  3. UVa 1001 奶酪里的老鼠(Dijkstra或Floyd)

    https://vjudge.net/problem/UVA-1001 题意:一个奶酪里有n个洞,老鼠在奶酪里的移动速度为10秒一个单位,但是在洞里可以瞬间移动.计算出老鼠从A点到达O点所需的最短时间 ...

  4. [BZOJ]|[Ural] Formula 1-----插头DP入门

    1519. Formula 1 Time limit: 1.0 secondMemory limit: 64 MB Background Regardless of the fact, that Vo ...

  5. 用java代码将数组元素顺序颠倒

    package test; public class Recover { public int[] reverse(int[] a) { int[] b = new int[a.length]; in ...

  6. Qt5_pro_01

    1. QT += core gui \ sql \ #ZC: 这个对应 #include <SQL/???> (如<QtSql/QSqlDatabase><QtSql/Q ...

  7. 对不队——Alpha冲刺

    第五天  日期:2018/6/20 一. 今日完成任务:专家审稿逻辑的开发 冯晓.马思远:会议网站栏目管理开发,软件功能测试 王爽.彭辉:审稿管理员分稿和稿件查找功能开发,博客撰写 吴琼.郝延婷:更换 ...

  8. dockfile杂项

    工程源代码+工程的配置文件 在外面配置好 1 工程的配置文件,是工程的一部分 2 要贯彻内聚原则, 用1句挂载整个工程. 在外面集中配置好在一个路径下,一起挂进去或者COPY进去. 防止先COPY了体 ...

  9. Codeforces C - Om Nom and Candies

    C - Om Nom and Candies 思路:贪心+思维(或者叫数学).假设最大值max(wr,wb)为wr,当c/wr小于√c时,可以枚举r糖的数量(从0到c/wr),更新答案,复杂度√c:否 ...

  10. English trip -- VC(情景课) 7 B Clothing 服装

    xu言: 不要使用中式的思维去思考西方的语义!!!切记切记 words a tie   领带 a blouse  女士衬衣 a sweater  毛衣 a skirt  短裙 a jacket   夹 ...