• priority

    • terminal

    • scope

    • controller

    • require

    • restrict

    • template

    • templateUrl

    • replace

    • transclude

    • compile

    • link


    现在我们开始一个一个地吃掉它们……,但是并不是按顺序讲的。


    priority

    这个值设置指令的权重,默认是 。当一个节点中有多个指令存在时,就按着权限从大到小的顺序依次执行它们的 compile 函数。相同权重顺序不定。

    terminal

    是否以当前指令的权重为结束界限。如果这值设置为 true ,则节点中权重小于当前指令的其它指令不会被执行。相同权重的会执行。

    restrict

    指令可以以哪些方式被使用,可以同时定义多种方式。

    • E 元素方式 <my-directive></my-directive>

    • A 属性方式 <div my-directive=”exp”> </div>


    • M 注释方式 <!— directive: my-directive exp —>



    transclude

    前面已经讲过基本的用法了。可以是 ‘element’true 两种值。

    compile

    基本的定义函数。 function compile(tElement, tAttrs, transclude) { … }

    link

    前面介绍过了。大多数时候我们不需要单独定义它。只有 compile 未定义时 link 才会被尝试。 function link(scope, iElement, iAttrs, controller) { … }

    scope

    scope 的形式。 false 节点的 scope , true 继承创建一个新的 scope , {} 不继承创建一个新的隔离 scope 。 {@attr: ‘引用节点属性’, =attr: ‘把节点属性值引用成scope属性值’, &attr: ‘把节点属性值包装成函数’}

    controller

    为指令定义一个 controller , function controller($scope, $element, $attrs, $transclude) { … }

    name

    指令的 controller 的名字,方便其它指令引用。

    require

    要引用的其它指令 conroller 的名字, ?name 忽略不存在的错误, ^name 在父级查找。

    template

    模板内容。

    templateUrl

    从指定地址获取模板内容。

    replace

    是否使用模板内容替换掉整个节点, true 替换整个节点, false 替换节点内容。

    1. var app = angular.module('Demo', [], angular.noop);

      app.directive('a', function(){
      var func = function(element, attrs, link){
      console.log('a');
      }

      return {compile: func,
      priority: 1,
      restrict: 'EA'};
      });

      app.directive('b', function(){
      var func = function(element, attrs, link){
      console.log('b');
      }

      return {compile: func,
      priority: 2,
      //terminal: true,
      restrict: 'A'};
      });


    上面几个参数值都是比较简单且容易理想的。



    再看 scope 这个参数:

    1. <div ng-controller="TestCtrl">
      <div a b></div>
      </div>
    1. 1 var app = angular.module('Demo', [], angular.noop);
      2
      3 app.directive('a', function(){
      4 var func = function(element, attrs, link){
      5 return function(scope){
      6 console.log(scope);
      7 }
      8 }
      9
      10 return {compile: func,
      11 scope: true,
      12 restrict: 'A'};
      13 });
      14
      15 app.directive('b', function(){
      16 var func = function(element, attrs, link){
      17 return function(scope){
      18 console.log(scope);
      19 }
      20 }
      21
      22 return {compile: func,
      23 restrict: 'A'};
      24 });
      25
      26 app.controller('TestCtrl', function($scope){
      27 $scope.a = '123';
      28 console.log($scope);
      29 });


    对于 scope


    • 默认为 falselink 函数接受的 scope 为节点所在的 scope

    • true 时,则 函数中第一个参数(还有 controller 参数中的 $scope ), scope 是节点所在的 scopechild scope ,并且如果节点中有多个指令,则只要其中一个指令是 true 的设置,其它所有指令都会受影响。


    这个参数还有其它取值。当其为 {} 时,则 link 接受一个完全隔离(isolate)的 scope ,于 true 的区别就是不会继承其它 scope 的属性。但是这时,这个 scope 的属性却可以有很灵活的定义方式:



    @attr 引用节点的属性。

    1. <div ng-controller="TestCtrl">
      <div a abc="here" xx="{{ a }}" c="ccc"></div>
      </div>
    1. var app = angular.module('Demo', [], angular.noop);

      app.directive('a', function(){
      var func = function(element, attrs, link){
      return function(scope){
      console.log(scope);
      }
      }

      return {compile: func,
      scope: {a: '@abc', b: '@xx', c: '@'},
      restrict: 'A'};
      });

      app.controller('TestCtrl', function($scope){
      $scope.a = '123';
      });

    • @abc 引用 div 节点的 abc 属性。

    • @xx 引用 div 节点的 xx 属性,而 xx 属性又是一个变量绑定,于是 scopeb 属性值就和 TestCtrla 变量绑定在一起了。

    • @ 没有写 attr name ,则默认取自己的值,这里是取 div 的 c 属性。


    =attr 相似,只是它把节点的属性值当成节点 scope 的属性名来使用,作用相当于上面例子中的 @xx

    1. <div ng-controller="TestCtrl">
      <div a abc="here"></div>
      </div>
    1. var app = angular.module('Demo', [], angular.noop);

      app.directive('a', function(){
      var func = function(element, attrs, link){
      return function(scope){
      console.log(scope);
      }
      }

      return {compile: func,
      scope: {a: '=abc'},
      restrict: 'A'};
      });

      app.controller('TestCtrl', function($scope){
      $scope.here = '123';
      });


    &attr 是包装一个函数出来,这个函数以节点所在的 scope 为上下文。来看一个很爽的例子:

    1. 1 var app = angular.module('Demo', [], angular.noop);
      2
      3 app.directive('a', function(){
      4 var func = function(element, attrs, link){
      5 return function llink(scope){
      6 console.log(scope);
      7 scope.a();
      8 scope.b();
      9
      10 scope.show = function(here){
      11 console.log('Inner, ' + here);
      12 scope.a({here: 5});
      13 }
      14 }
      15 }
      16
      17 return {compile: func,
      18 scope: {a: '&abc', b: '&ngClick'},
      19 restrict: 'A'};
      20 });
      21
      22 app.controller('TestCtrl', function($scope){
      23 $scope.here = 123;
      24 console.log($scope);
      25
      26 $scope.show = function(here){
      27 console.log(here);
      28 }
      29 });


    scope.a 是 &abc ,即:



      scope.b 是 &ngClick ,即:

      1. scope.b = function(){show(here)}


      这里的 show()here 都是 TestCtrl 的,于是上面的代码最开始会在终端输出一个 124



      当点击“这里”时,这时执行的 show(here) 就是 llink 中定义的那个函数了,与 无关。但是,其间的 scope.a({here:5}) ,因为 a 执行时是 TestCtrl 的上下文,于是向 a 传递的一个对象,里面的所有属性 TestCtrl 就全收下了,接着执行 here=here+1 ,于是我们会在屏幕上看到 6



      这里是一个上下文交错的环境,通过 & 这种机制,让指令的 scope 与节点的 scope 发生了互动。真是鬼斧神工的设计。而实现它,只用了几行代码:

      1. case '&': {
        parentGet = $parse(attrs[attrName]);
        scope[scopeName] = function(locals) {
        return parentGet(parentScope, locals);
        }
        break;
        }


      再看 controller 这个参数。这个参数的作用是提供一个 controller 的构造函数,它会在 compile 函数之后, link 函数之前被执行。

      1. <a>haha</a>
      1. 1 var app = angular.module('Demo', [], angular.noop);
        2
        3 app.directive('a', function(){
        4 var func = function(){
        5 console.log('compile');
        6 return function(){
        7 console.log('link');
        8 }
        9 }
        10
        11 var controller = function($scope, $element, $attrs, $transclude){
        12 console.log('controller');
        13 console.log($scope);
        14
        15 var node = $transclude(function(clone_element, scope){
        16 console.log(clone_element);
        17 console.log('—');
        18 console.log(scope);
        19 });
        20 console.log(node);
        21 }
        22
        23 return {compile: func,
        24 controller: controller,
        25 transclude: true,
        26 restrict: 'E'}
        27 });


      controller 的最后一个参数, $transclude ,是一个只接受 cloneAttachFn 作为参数的一个函数。



      按官方的说法,这个机制的设计目的是为了让各个指令之间可以互相通信。参考普通节点的处理方式,这里也是处理指令 scope 的合适位置。

      1. <a b>kk</a>


      name 参数在这里可以用以为 controller 重起一个名字,以方便在 require 参数中引用。



      require 参数可以带两种前缀(可以同时使用):


      • ? ,如果指定的 controller 不存在,则忽略错误。即:


        1. require: '?not_b'




        如果名为 not_b 的 controller 不存在时,不会直接抛出错误, link 函数中对应的 $controllerundefined



      • ^ ,同时在父级节点中寻找指定的 controller ,把上面的例子小改一下:



        1. <a><b>kk</b></a>




        arequire 改成(否则就找不到 not_a 这个 controller ):



        1. require: '?^not_a'





      还剩下几个模板参数:


      template 模板内容,这个内容会根据 replace 参数的设置替换节点或只替换节点内容。


      templateUrl 模板内容,获取方式是异步请求。


      replace 设置如何处理模板内容。为 true 时为替换掉指令节点,否则只替换到节点内容。


      1. <div ng-controller="TestCtrl">
        <h1 a>原始内容</h1>
        </div>
      1. var app = angular.module('Demo', [], angular.noop);

        app.directive('a', function(){
        var func = function(){
        }

        return {compile: func,
        template: '<p>标题 {{ name }} <button ng-click="name=\'hahaha\'">修改</button></p>',
        //replace: true,
        //controller: function($scope){$scope.name = 'xxx'},
        //scope: {},
        scope: true ,
        controller: function($scope){console.log($scope)},
        restrict: 'A'}
        });

        app.controller('TestCtrl', function($scope){
        $scope.name = '123';
        console.log($scope);
        });


      template 中可以包括变量引用的表达式,其 scope 遵寻 scope 参数的作用(可能受继承关系影响)。



      templateUrl 是异步请求模板内容,并且是获取到内容之后才开始执行指令的 compile 函数。



      最后说一个 compile 这个参数。它除了可以返回一个函数用为 link 函数之外,还可以返回一个对象,这个对象能包括两个成员,一个 pre ,一个 post 。实际上, 函数是由两部分组成,所谓的 preLinkpostLink 。区别在于执行顺序,特别是在指令层级嵌套的结构之下, postLink 是在所有的子级指令 link 完成之后才最后执行的。 compile 如果只返回一个函数,则这个函数被作为 postLink 使用:

      1. <a><b></b></a>
      1. 1 var app = angular.module('Demo', [], angular.noop);
        2
        3 app.directive('a', function(){
        4 var func = function(){
        5 console.log('a compile');
        6 return {
        7 pre: function(){console.log('a link pre')},
        8 post: function(){console.log('a link post')},
        9 }
        10 }
        11
        12 return {compile: func,
        13 restrict: 'E'}
        14 });
        15
        16 app.directive('b', function(){
        17 var func = function(){
        18 console.log('b compile');
        19 return {
        20 pre: function(){console.log('b link pre')},
        21 post: function(){console.log('b link post')},
        22 }
        23 }
        24
        25 return {compile: func,
        26 restrict: 'E'}
        27 });