AngularJS 指令定义时的参数
指令定义时的参数如下:
- 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 -->
- E 元素方式
- 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>
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: true, restrict: 'A'}; }); app.directive('b', function(){ var func = function(element, attrs, link){ return function(scope){ console.log(scope); } } return {compile: func, restrict: 'A'}; }); app.controller('TestCtrl', function($scope){ $scope.a = '123'; console.log($scope); });
对于 scope :
- 默认为
false
,link
函数接受的scope
为节点所在的scope
。 - 为
true
时,则link
函数中第一个参数(还有controller
参数中的$scope
),scope
是节点所在的 scope 的 child 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 属性又是一个变量绑定,于是
scope
中b
属性值就和TestCtrl
的a
变量绑定在一起了。 - @ 没有写 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>
var app = angular.module('Demo', [], angular.noop); app.directive('a', function(){ var func = function(element, attrs, link){ return function llink(scope){ console.log(scope); scope.a(); scope.b(); scope.show = function(here){ console.log('Inner, ' + here); scope.a({here: 5}); } } } return {compile: func, scope: {a: '&abc', b: '&ngClick'}, restrict: 'A'}; }); app.controller('TestCtrl', function($scope){ $scope.here = 123; console.log($scope); $scope.show = function(here){ console.log(here); } });
scope.a 是 &abc ,即:
scope.a = function(){here = here + 1}
只是其中的 here
是 TestCtrl
的。
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>
var app = angular.module('Demo', [], angular.noop); app.directive('a', function(){ var func = function(){ console.log('compile'); return function(){ console.log('link'); } } var controller = function($scope, $element, $attrs, $transclude){ console.log('controller'); console.log($scope); var node = $transclude(function(clone_element, scope){ console.log(clone_element); console.log('--'); console.log(scope); }); console.log(node); } return {compile: func, controller: controller, transclude: true, restrict: 'E'} });
controller
的最后一个参数, $transclude
,是一个只接受 cloneAttachFn
作为参数的一个函数。
按官方的说法,这个机制的设计目的是为了让各个指令之间可以互相通信。参考普通节点的处理方式,这里也是处理指令 scope
的合适位置。
<a b>kk</a>
var app = angular.module('Demo', [], angular.noop); app.directive('a', function(){ var func = function(){ } var controller = function($scope, $element, $attrs, $transclude){ console.log('a'); this.a = 'xx'; } return {compile: func, name: 'not_a', controller: controller, restrict: 'E'} }); app.directive('b', function(){ var func = function(){ return function($scope, $element, $attrs, $controller){ console.log($controller); } } var controller = function($scope, $element, $attrs, $transclude){ console.log('b'); } return {compile: func, controller: controller, require: 'not_a', restrict: 'EA'} });
name 参数在这里可以用以为 controller
重起一个名字,以方便在 require 参数中引用。
require 参数可以带两种前缀(可以同时使用):
- ? ,如果指定的 controller 不存在,则忽略错误。即:require: '?not_b'
如果名为
not_b
的 controller 不存在时,不会直接抛出错误,link
函数中对应的$controller
为undefined
。 - ^ ,同时在父级节点中寻找指定的 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>
var app = angular.module('Demo', [], angular.noop); app.directive('a', function(){ var func = function(){ console.log('a compile'); return { pre: function(){console.log('a link pre')}, post: function(){console.log('a link post')}, } } return {compile: func, restrict: 'E'} }); app.directive('b', function(){ var func = function(){ console.log('b compile'); return { pre: function(){console.log('b link pre')}, post: function(){console.log('b link post')}, } } return {compile: func, restrict: 'E'} });
更多建议: