开门见山地说,scope:{}使指令与外界隔离开来,使其模板(template)处于non-inheriting(无继承)的状态,固然除非你在其中使用了transclude嵌入,这点以后的笔记会再详细记录的。可是这显然不符合实际开发中的需求,由于实际上,咱们常常想要咱们的指令可以在特定的状况下与外界进行数据上的交互,这就须要借助绑定策略之手了。
你们知道,当scope选项写为scope:{}这种形式的时候,就已经为指令生成了隔离做用域,如今,咱们来看看绑定策略的三种形式:& 、= 、@。html
首先是@,它将本地做用域和DOM中的属性值绑定起来(且这个属性的值必须是父级做用域中的),什么意思呢?说的简单一点就是假设你在模板中有个双花括号表达式,而后咱们把表达式里的内容和html中指令里特定名字的属性绑定起来,仍是不懂?看看下面的代码:express
JS代码:数组
directive("direct",function(){ return{ restrict: 'ECMA', template: '<div>指令中:{{ name }}</div>', scope:{ name:'@forName' } } }) .controller("nameController",function($scope){ $scope.Name="张三"; });
HTML代码:函数
<div ng-controller="nameController"> <direct for-name="{{ Name }}"></direct> <div>
运行结果可想而知,{{ name }}成功地与父控制器中的Name绑定起来了。固然这里也能够这样写name:'@' 这样写的话,就默认DOM中的属性名为name了意即 for-name="{{ Name }}"可简写为name="{{ Name }}";其实,另外两个符号=和&也有这样的简写规则,方便起见接下来都使用这种写法。
@到此为止,接下来就是'='了。=与@的不一样点在于,@是针对字符串(准确来讲是表达式expression)而用,但=是针对某个对象的引用;双向绑定
这么说可能不太专业,但就拿上边的例子而言,咱们在html中,把Name这个字符串经过一对双花括号传递给for-name属性,但若是咱们用了=,这里传入的Name就不该该是一个字符串,而是一个对象的引用。这不是一个很一目了然的概念,因此我用接下来的两个例子诠释它的含义。rest
第一个例子:数组中的对象的引用code
JS代码:htm
directive("direct",function(){ return{ restrict: 'ECMA', template: '<div>指令中:{{ case.name }}</div>', scope:{ case:'=' } } }) .controller("nameController",function($scope){ $scope.data=[{name:"张三"},{name:"李四"}]; });
HTML代码:对象
<div ng-controller="nameController"> <direct case="data[0]"></direct> <direct case="data[1]"></direct> <div>
结果就是,一个张三,一个李四。这个例子中,data是一个对象数组,里面包含了两个对象,因此,咱们分别把两个对象传递给了case这个属性,case属性就把这个对象的引用传递给了模板中咱们写的{{ case.name }}中的case;而若是你在=后边加上了本身定义的名字,那只要把html里case属性换成那个名字就能够了。继承
第二个例子:经典的双向输入框
按照Angular的入门案例,建立两个双向绑定的输入框,最简单的实现方式就是:
<input ng-model="test"/> <input ng-model="test"/>
使用ng-model指令就能够作到了。接着,咱们在本身的指令中实现这个效果。
JS代码:
directive("direct",function(){ return{ restrict: 'ECMA', template: '<div>指令中:<input ng-model="model"/></div>', scope:{ model:'=' } } }) .controller("nameController",function($scope){ $scope.data=[{name:"张三"},{name:"李四"}]; });
HTML代码:
<div ng-controller="nameController"> 父级scope中:<input ng-model="mark"/> <direct model="mark"/></direct> </div>
这就完成了,其实只不过是加了一点小把戏,把ng-model换成了model而已。
注意到,这两个例子中,都是使用对象的引用,而不是单纯的字符串,这也是=能够进行双向绑定的关键。
最后是&符号。它的含义是:对父级做用域进行绑定,并将其中的属性包装成一个函数,注意,是属性,意即,任何类型的属性都会被包装成一个函数,好比一个单纯的字符串,或是一个对象数组,或是一个函数方法。
若是是字符串、对象数组和无参的函数,那么可想而知,它们都会被包装成一个无参的函数,如果有参的函数方法则反之,而且咱们须要为其传入一个对象。如今,分别针对有参和无参两种状况举例。
无参状况↓
JS代码:
.directive("direct",function(){ return{ restrict: 'ECMA', template: '<div>{{ title }}</div>'+'<div><ul><li ng-repeat="x in contents">{{ x.text }}</li></ul></div>', scope:{ getTitle:'&', getContent:'&' }, controller:function($scope){ $scope.title=$scope.getTitle(); //调用无参函数 $scope.contents=$scope.getContent(); //调用无参函数 } } }) .controller("nameController",function($scope){ $scope.title="标题"; $scope.contents =[{text:1234},{text:5678}]; });
HTML代码:
<div ng-controller="nameController"> <direct get-title="title" get-content="contents"></direct> </div>
这个例子有几个注意点:
1.指令的本地属性(即模板里花括号中的属性)须要从本地取值,因此使用了controller选项,而在controller选项中,两个无参方法分别返回了父级scope中的title字符串和contents对象数组。
2.在HTML中,咱们把设置了get-title和get-content的属性值为title和contents,这实际上就完成了与父级scope的绑定,由于咱们才能够从那儿取得实质的内容。
OK,有参状况↓
JS代码:
.directive("direct",function(){ return{ restrict: 'ECMA', template: ' <div><input ng-model="model"/></div> '+' < div><button ng-click="show({name:model})">show</button>', scope:{ show:'&' } } }) .controller("nameController",function($scope){ $scope.showName=function(name){ alert(name); } });
HTML代码:
<div ng-controller="nameController"> <direct show="showName(name)"></direct> </div>
这个例子中,经过模板中的ng-click触发了show函数并将一个叫作model的对象做为name参数传递了进去,而在html中,咱们把show的属性值设为showName(name)。这其中的道理跟无参的例子是大同小异的。
总结:
为何Angular要为咱们提供这样一套绑定策略呢?就是由于它想让咱们在为指令建立隔离做用域的同时,还能访问到父级中的属性,这就像,你在隔离做用域身上打了一个洞,而后用一条管道,把指令内部和外界的属性给连起来(绑定),而且一切的通讯都只能经过这条管道来实行。这是我目前能作到的最深入的理解了,可能还有须要的补充和纠正的地方,但愿你们能抽空指点我一下,感激涕零!