原文链接: https://segmentfault.com/a/1190000002773689
每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部的Controller提供的作用域或者根作用域($rootScope)),还是创建一个新的自己的作用域,当然AngularJS为我们指令的scope
参数提供了三种选择,分别是:false
,true
,{}
;默认情况下是false
。
一,
scope = false 在指令模板中可以直接使用父作用域中的变量,函数
<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>
</div>
<div class="my-directive" my-directive></div>
</div>
</div>
<script>
angular.module("MyApp",[])
.controller("MyController",function($scope){
$scope.name="aaa";
$scope.age=20;
$scope.changeAge=function(){
$scope.age=10;
}
})
.directive('myDirective',function(){
var obj={
restrict:"AE",
scope:false, //默认值,共享父级作用域
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'"+
"</div>"
}
return obj;
})
</script>
因为我们将scope
的属性设置为false
所以,我们创建的指令继承了父作用域的一切属性和方法,这也使得在指令的模板中我们可以使用这些属性和方法。
二,scope=true 当把scope
属性设置为true
时,这表明我们创建的指令要创建一个新的作用域,这个作用域继承自我们的父作用域。
然后我们再试着在我们的input
输入框中写一些字符串,会发现,指令中的那个name
发生了变化,但是指令外的那个name
却没有发生变化,这说明了一个问题。
- 当我们将
scope
设置为true
的时候,我们就新创建了一个作用域,只不过这个作用域是继承了我们的父作用域;我觉得可以这样理解,我们新创建的作用域是一个新的作用域,只不过在初始化的时候,用了父作用域的属性和方法去填充我们这个新的作用域。它和父作用域不是同一个作用域。 - 当我们将
scope
设置为false
的时候,我们创建的指令和父作用域(其实是同一个作用域)共享同一个model
模型,所以在指令中修改模型数据,它会反映到父作用域的模型中。
三, scope = {}
AngularJS最强的大的地方之一就是它可以构建组建,无论放在哪里都是可以使用的;
这所以可以做到这些,不得不归功于指令的这个属性;当我们将scope
设置为{}
时,意味着我们创建的一个新的与父作用域隔离的新的作用域,这使我们在不知道外部环境的情况下,就可以正常工作,不依赖外部环境。
<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>
</div>
<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>
</div>
</div>
<script>
angular.module("MyApp",[])
.controller("MyController",function($scope){
$scope.name="aaa";
$scope.age=20;
$scope.changeAge=function(){
$scope.age=10;
}
})
.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;
})
</script>
我们使用了隔离的作用域,不代表我们不可以使用父作用域的属性和方法。
- 我们可以通过向
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>
,注意,属性的名字要用-
将多个个单词连接。
注意:在新创建指令的作用域对象中,使用属性的名字进行绑定时,要使用驼峰命名标准,比如下面的代码。
scope: {
// `myName` 就是原来元素中的`my-name`属性
name: '@myName',
age: '=',
// `changeMyAge`就是原来元素中的`change-my-age`属性
changeAge: '&changeMyAge'
}
进一步说明,我们的指令是如何利用这些前缀标识符来寻找我们想要的属性或者函数的?
@
当指令编译到模板的name
时,就会到scope
中寻找是否含有name
的键值对,如果存在,就像上面那样,看到@
就知道这是一个单向的数据绑定,然后寻找原来的那个使用指令的元素上(或者是指令元素本身)含有这个值的属性即my-name={{name}}
,然后在父作用域查找{{name}}
的值,得到之后传递给模板中的name
。=
和&
与@
差不多,只不过=
进行的是双向的数据绑定,不论模板还是父作用域上的属性的值发生改变都会使另一个值发生改变,而&
是绑定函数而已。