返回介绍

指令定义时的参数

发布于 2024-12-24 22:41:42 字数 23805 浏览 0 评论 0 收藏 0

18.8. 指令定义时的参数

指令定义时的参数如下:

  • name
  • priority
  • terminal
  • scope
  • controller
  • require
  • restrict
  • template
  • templateUrl
  • replace
  • transclude
  • compile
  • link

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

`priority`
这个值设置指令的权重,默认是 0 。当一个节点中有多个指令存在时,就按着权限从大到小的顺序依次执行它们的 compile 函数。相同权重顺序不定。
`terminal`
是否以当前指令的权重为结束界限。如果这值设置为 true ,则节点中权重小于当前指令的其它指令不会被执行。相同权重的会执行。
`restrict`
指令可以以哪些方式被使用,可以同时定义多种方式。
  • E 元素方式 <my-directive></my-directive>
  • A 属性方式 <div my-directive="exp"> </div>
  • C 类方式 <div class="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 替换节点内容。
  <a b></a>
  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` 这个参数:

  <div ng-controller="TestCtrl">
    <div a b></div>
  </div>
 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 时,则 link 函数中第一个参数(还有 controller 参数中的 $scope ), scope 是节点所在的 scopechild scope ,并且如果节点中有多个指令,则只要其中一个指令是 true 的设置,其它所有指令都会受影响。

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

`@attr` 引用节点的属性。

  <div ng-controller="TestCtrl">
    <div a abc="here" xx="{ { a } }" c="ccc"></div>
  </div>
  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` :

  <div ng-controller="TestCtrl">
    <div a abc="here"></div>
  </div>
  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 为上下文。来看一个很爽的例子:

  <div ng-controller="TestCtrl">
    <div a abc="here = here + 1" ng-click="show(here)">这里</div>
    <div>{ { here } }</div>
  </div>
 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.a = function(){here = here + 1}

只是其中的 hereTestCtrl 的。

scope.b 是 `&ngClick` ,即:

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

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

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

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

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

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

  <a>haha</a>
 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 的合适位置。

  <a b>kk</a>
 1   var app = angular.module('Demo', [], angular.noop);
 2   
 3   app.directive('a', function(){
 4     var func = function(){
 5     }
 6   
 7     var controller = function($scope, $element, $attrs, $transclude){
 8       console.log('a');
 9       this.a = 'xx';
10     }
11   
12     return {compile: func,
13             name: 'not_a',
14             controller: controller,
15             restrict: 'E'}
16   });
17   
18   app.directive('b', function(){
19     var func = function(){
20       return function($scope, $element, $attrs, $controller){
21         console.log($controller);
22       }
23     }
24   
25     var controller = function($scope, $element, $attrs, $transclude){
26       console.log('b');
27     }
28   
29     return {compile: func,
30             controller: controller,
31             require: 'not_a',
32             restrict: 'EA'}
33   });

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

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

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

      require: '?not_b'
    

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

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

      <a><b>kk</b></a>
    

    a 的 `require` 改成(否则就找不到 not_a 这个 controller ):

      require: '?^not_a'
    

还剩下几个模板参数:

`template` 模板内容,这个内容会根据 `replace` 参数的设置替换节点或只替换节点内容。
`templateUrl` 模板内容,获取方式是异步请求。
`replace` 设置如何处理模板内容。为 true 时为替换掉指令节点,否则只替换到节点内容。
  <div ng-controller="TestCtrl">
    <h1 a>原始内容</h1>
  </div>
  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` 。实际上, link 函数是由两部分组成,所谓的 `preLink` 和 `postLink` 。区别在于执行顺序,特别是在指令层级嵌套的结构之下, `postLink` 是在所有的子级指令 link 完成之后才最后执行的。 `compile` 如果只返回一个函数,则这个函数被作为 `postLink` 使用:

  <a><b></b></a>
 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   });

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文