学习了AngularJS好长时间,最近再次回首看看指令这部分的时候,以为比本身刚开始学习的时候理解的更加深刻了,尤为是指令的做用域这部分。javascript
当初看的是《AngularJS权威指南》这本书,可是感受这本书关于这方面讲的不是很细致,
另外吐槽一下,这本书中文版印刷的质量不是很好,不少地方都有错误;不过讲的仍是能够的,是一本学习AngularJS的好书。css
下面咱们就来详细分析一下指令的做用域。
在这以前但愿你对AngularJS的Directive有必定的了解,否则你对下面部分的理解可能会有一点难度。html
步入正题:java
每当一个指令被建立的时候,都会有这样一个选择,是继承本身的父做用域(通常是外部的Controller提供的做用域或者根做用域($rootScope)),仍是建立一个新的本身的做用域,固然AngularJS为咱们指令的
scope
参数提供了三种选择,分别是:false
,true
,{}
;默认状况下是false
。网络
首先咱们来看一下,当scope
参数被设置为false
时有什么状况发生app
在这种状况下,在指令模板中能够直接使用父做用域中的变量,函数
首先咱们来建立一个指令,代码以下所示:
JS代码:函数
javascriptangular.module("MyApp", []) .controller("MyController", function ($scope) { //J1 这里咱们在做用域里初始化两个变量 $scope.name = "dreamapple"; $scope.age = 20; //J2 建立一个方法,修改咱们建立的对象的年龄 $scope.changeAge = function () { $scope.age = 22; } }) //J3 建立咱们的指令,指令名字为"myDirective" .directive("myDirective", function () { var obj = { //J4 指令的声明模式为 "AE" 属性和元素 restrict: "AE", //J5 指令继承父做用域的属性和方法 scope: false, replace: true, template: "<div class='my-directive'>" + "<h3>下面部分是咱们建立的指令生成的</h3>" + "个人名字是:<span ng-bind='name'></span><br/>" + "个人年龄是:<span ng-bind='age'></span>" + "<input type='text' ng-model='name'>"+ " </div>" } return obj; });
HTML代码:学习
html
<div ng-app="MyApp"> <div class="container" ng-controller="MyController"> <div class="my-info">个人名字是:<span ng-bind="name"></span> <!-- 使用"ng-bind"防止网络状态不佳时出现没有被赋值表达式 --> <br/>个人年龄是:<span ng-bind="age"></span> </div> <!-- 使用属性声明指令 --> <div class="my-directive" my-directive></div> </div> </div>
CSS代码:spa
cssdiv{ padding: 6px; } div.container { border: 1px solid black; } div.my-info { border: 1px solid blue; } div.my-directive{ border: 1px solid green; }
下面咱们来详细解释一下上面的代码:
由于咱们将
scope
的属性设置为false
因此,咱们建立的指令继承了父做用域的一切属性和方法,这也使得在指令的模板中咱们可使用这些属性和方法。
注意:此时咱们在输入框里改变名字,会发现上面的两个名字都发生了变化,你确定会说,这确定是这样啊,数据绑定嘛,好,咱们接着往下走。
当把scope
属性设置为true
时,这代表咱们建立的指令要建立一个新的做用域,这个做用域继承自咱们的父做用域。
等等,刚才咱们不是说了,当把scope
属性值设置为false
时,不也是继承咱们的父做用域吗?表急,咱们接着往下看。
修改上面的JS代码,将指令中的:
scope:false
修改成scope:true
而后咱们再试着在咱们的input
输入框中写一些字符串,会发现,指令中的那个name
发生了变化,可是指令外的那个name
却没有发生变化,这说明了一个问题。
- 当咱们将
scope
设置为true
的时候,咱们就新建立了一个做用域,只不过这个做用域是继承了咱们的父做用域;我以为能够这样理解,咱们新建立的做用域是一个新的做用域,只不过在初始化的时候,用了父做用域的属性和方法去填充咱们这个新的做用域。它和父做用域不是同一个做用域。- 当咱们将
scope
设置为false
的时候,咱们建立的指令和父做用域(实际上是同一个做用域)共享同一个model
模型,因此在指令中修改模型数据,它会反映到父做用域的模型中。
Online Code Part2
scope = {}
下面咱们要进入一个好玩的部分,当咱们将scope
的属性设置为{}
时,咱们能够作更多的事情。
AngularJS最强的大的地方之一就是它能够构建组建,不管放在哪里都是可使用的;
这因此能够作到这些,不得不归功于指令的这个属性;当咱们将scope
设置为{}
时,意味着咱们建立的一个新的与父做用域隔离的新的做用域,这使咱们在不知道外部环境的状况下,就能够正常工做,不依赖外部环境。
固然首先咱们仍是要给出咱们的例子,先看代码,咱们修改了上述的JS代码和HTML代码
JS代码:
javascriptangular.module("MyApp", []) .controller("MyController", function ($scope) { $scope.name = "dreamapple"; $scope.age = 20; $scope.changeAge = function(){ $scope.age = 0; } }) .directive("myDirective", function () { var obj = { restrict: "AE", scope: { name: '@myName', age: '=', changeAge: '&changeMyAge' }, replace: true, template: "<div class='my-directive'>" + "<h3>下面部分是咱们建立的指令生成的</h3>" + "个人名字是:<span ng-bind='name'></span><br/>" + "个人年龄是:<span ng-bind='age'></span><br/>" + "在这里修更名字:<input type='text' ng-model='name'><br/>" + "<button ng-click='changeAge()'>修改年龄</button>" + " </div>" } return obj; });
HTML代码:
html
<div ng-app="MyApp"> <div class="container" ng-controller="MyController"> <div class="my-info">个人名字是:<span ng-bind="name"></span> <br/>个人年龄是:<span ng-bind="age"></span> <br /> </div> <div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div> </div> </div>
咱们使用了隔离的做用域,不表明咱们不可使用父做用域的属性和方法。
scope
的{}
中传入特殊的前缀标识符(即prefix
),来进行数据的绑定。@
,&
,=
引用应用指令的元素的属性,如上面的代码那样,咱们能够在<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>
这个元素中,利用前缀标识符经过使用属性my-name
,age
,change-my-age
来引用这些属性的值。下面咱们来看看如何使用这些前缀标识符:
@
这是一个单项绑定的前缀标识符
使用方法:在元素中使用属性,比如这样<div my-directive my-name="{{name}}"></div>
,注意,属性的名字要用-
将两个单词链接,由于是数据的单项绑定因此要经过使用{{}}
来绑定数据。
=
这是一个双向数据绑定前缀标识符
使用方法:在元素中使用属性,比如这样<div my-directive age="age"></div>
,注意,数据的双向绑定要经过=
前缀标识符实现,因此不可使用{{}}
。
&
这是一个绑定函数方法的前缀标识符
使用方法:在元素中使用属性,比如这样<div my-directive change-my-age="changeAge()"></div>
,注意,属性的名字要用-
将多个个单词链接。
注意:在新建立指令的做用域对象中,使用属性的名字进行绑定时,要使用驼峰命名标准,好比下面的代码。
javascriptscope: { // `myName` 就是原来元素中的`my-name`属性 name: '@myName', age: '=', // `changeMyAge`就是原来元素中的`change-my-age`属性 changeAge: '&changeMyAge' }
进一步说明,咱们的指令是如何利用这些前缀标识符来寻找咱们想要的属性或者函数的?
@
当指令编译到模板的name
时,就会到scope
中寻找是否含有name
的键值对,若是存在,就像上面那样,看到@
就知道这是一个单向的数据绑定,而后寻找原来的那个使用指令的元素上(或者是指令元素自己)含有这个值的属性即my-name={{name}}
,而后在父做用域查找{{name}}
的值,获得以后传递给模板中的name
。=
和&
与@
差很少,只不过=
进行的是双向的数据绑定,不论模板仍是父做用域上的属性的值发生改变都会使另外一个值发生改变,而&
是绑定函数而已。到这里咱们关于AngularJS指令的做用域估计也理解的差很少了,那好,洗洗睡吧!