理解Angular中的$scope
Angular scope 使用的一些问题总结
在AngularJS中,child scope一般通过原型式继承自它的parent scope。(directive的scope属性为{...}的情况除外,因为它会创建独立的scope)。在指令中,默认使用的是parent scope,这意味着在指令中改变的任何属性都会反映到其parent scope中。设置scope:true 会在该指令中使用原型继承。
scope的继承通常是直截了当的,但是当你尝试在child scope中使用定义在parent scope的双向数据绑定的时候,它可能就不像你期望的那样起作用了。原因是child scope会重写parent scope中的重名属性,这是原型式继承的一个特征。
注意:ng-repeat, ng-switch, ng-view, ng-include 都会创建新的child scope,上述问题通常出现在这些指令的使用过程中。具体可以看看这个例子
要避免上面的问题有以下几个方法:
- 总是在ng-model中使用 '.' 即ng-model使用一个对象而不是一个基本类型的值
这样:<input type="text" ng-model="someObj.prop1">
而不是: <input type="text" ng-model="prop1"> - 如果你实在要使用基本类型,可以参照下面两个方法
1.在child scope中使用 $parent.parentScopeProperty,它会阻止child scope创建自己的属性覆盖parent scope上的同名属性
2.在parent scope中定义一个方法,然后在child scope中调用,将基本类型值通过参数传递给parent
Angular scope 的继承
- 以下会创建新的scope,并且原型式继承自parent scope:
ng-repeat, ng-include, ng-switch, ng-view, ng-controller, 设置了scope: true的指令, 设置了transclude: true的指令 - 以下会创建新的scope,但是不通过原型式继承自parent scope
设置了 scope: {....}. 这样会创建一个独立的scope
注意,默认情况下,自定义指令并不会创建新的scope,scope:false是默认值
ng-include
假设在controller.js中有以下代码
// 定义一个基本类型的值 $scope.myPrimitive = 50; // 定义一个对象 $scope.myObject = {aNumber: 11};
html代码
<script type="text/ng-template" id="/tpl1.html"> <input ng-model="myPrimitive"> </script> <div ng-include src="'/tpl1.html'"></div> <script type="text/ng-template" id="/tpl2.html"> <input ng-model="myObject.aNumber"> </script> <div ng-include src="'/tpl2.html'"></div>
上面每个ng-include都会产生一个新的child scope, 原型继承自parent scope

看看结果如下:

第一个input的ng-model是一个基本类型,它不能与parent scope中的表达式实现双向数据绑定,原因在于child scope通过原型继承属性自其parent scope,对于基本类型的值,child scope重写parent scope中的重名属性。

第二个input的ng-model是一个对象,它可以实现双向数据绑定,当ngModel查找一个对象时,它会一直查找到parent scope.

我们也可以对第一个input进行改写如下:
<input ng-model="$parent.myPrimitive">

这里$parent是child scope下的一个属性,该属性指向parent scope
或者在parent scope中定义一个方法来改变基本类型的值,在child scope调用该方法,看代码
// controller.js $scope.setMyPrimitive = function (value){ $scope.myPrimitive = value; }
<input ng-model="myPrimitive" class="form-control" ng-change="setMyPrimitive(myPrimitive)">
效果如下:可以看到可以正常地进行双向数据绑定了

ng-repeat
假设有如下代码:controller.js
$scope.myArrayOfPrimitives = [ 11, 22 ];
$scope.myArrayOfObjects = [{num: 101}, {num: 202}]
html代码如下:
<ul><li ng-repeat="num in myArrayOfPrimitives"> <input ng-model="num"></input> </li> </ul> <ul><li ng-repeat="obj in myArrayOfObjects"> <input ng-model="obj.num"></input> </li> </ul>
对每一个li,ng-repeat都创建一个新的scope,继承自parent scope,但是同时会将遍历的变量(第一个ul中为num) 作为新的child scope的一个属性,myArrayOfPrimitives[num]的值作为该属性的属性值。
angular中ng-repeat的源码如下:
childScope = scope.$new(); // child scope prototypically inherits from parent scope ... childScope[valueIdent] = value; // creates a new childScope property
如果在遍历数组中的每个item是一个基本类型(myArrayOfPrimitives),该item的值会被分配给新child scope的属性,改变这个属性的值(例如使用ng-model),不会改变parent scope中的数组。在第一个ul中,每个child scope有一个num属性独立于数组myArrayOfPrimitives。

如果我们希望改变在item中改变parent scope中的数组,我们需要将model变为一个数组对象
所以,如果数组中的每个item都是一个对象,该对象的一个引用会被分配给child scope的属性,改变child scope的属性值也会改变parent scope中的数组的引用。

今天先搞到这里,原文地址 Understanding Scopes

浙公网安备 33010602011771号