在AngularJS框架中深度探索范围对象的超级教程
首先,使用AngularJS嵌套控制器时遇到了问题。因为每个控制器都有其对应的作用域(相当于作用域和控制范围),所以控制器的嵌套意味着作用域的嵌套。此时,如果在两个作用域中有一个同名的模型,会发生什么?如何从子范围更新父范围中的模型?
这个问题很典型。例如,如果当前页面是产品列表,那么就需要定义一个ProductListController。
function Produclistcontroller($ scope,$ http){ $ http . get('/API/products . JSON ')。成功(函数(数据){ $scope.productList=数据;});$ scope . selected product={ };}您可能看到在“范围”中也定义了一个选定的产品模型,表明选择了某个产品。此时,将获得产品详细信息,页面将由AngularJS中的$routeProvider自动更新,并会拉出一个新的详细信息页面模板,其中有一个ProductDetailController。
函数ProductDetailController($scope,$http,$ RouteParams){ $ http . get('/API/products/' $ RouteParams . ProductID '。JSON’)。成功(函数(数据){ $ scope.selectedProduct=data});}有趣的事情发生了。这里还有一个精选产品。它将如何影响产品列表控制器中的选定产品?
答案是没有效果。在AnuglarJS中,子Scope确实继承了父Scope中的对象,但是当您尝试基本数据类型(字符串、数字、布尔)的双向数据绑定时,您会发现一些奇怪的行为,继承并没有像您想象的那样工作。子范围的属性隐藏(覆盖)父范围中具有相同名称的属性,对子范围属性(表单元素)的更改不会更新父范围属性的值。实际上,这种行为并不是AngularJS独有的,这就是JavaScript本身的原型链是如何工作的。一般来说,开发人员没有意识到ng-repeat、ng-switch、ng-view和ng-include都创建了它们的新子范围,所以他们在使用这些指令时经常会遇到问题。
第二,解决方案解决方案不是使用基本数据类型,而是始终在模型中再添加一个点。
使用
请改用type=' text ' ng-model=' some obj . prop 1 '。
输入类型=' text' ng-model=' prop1 '是个坏主意吗?下面的例子清楚地表达了我想表达的奇妙现象。
app . controller(' parent controller ',函数($ scope){ $ scope . parent primitive=' some primitive ' $ scope . parent obj={ };$ scope . ParentObj . ParentProperty=“某些值”;});app . controller(' child controller ',函数($ scope){ $ scope . parentprimitive='这不会修改父级' $ scope . parentobj . parentproperty='这将修改父级';});在线查看DEMO,但我真的,真的需要使用原始数据类型,如字符串编号。我该怎么办?2方法——。
在作用域中使用$ parent . parent原语。这将阻止作用域创建自己的属性。在父作用域中定义一个函数,让子作用域调用,并将原始数据类型的参数传递给父作用域,从而更新父作用域中的属性。(不总是可行的)第三,JavaScript原型链的继承结束了,让我们对JavaScript原型链有一个深刻的认识。这非常重要,尤其是当您从服务器端开发转向前端时,您应该熟悉Classic类继承。让我们回顾一下。
假设父类parentScope具有以下成员属性:字符串、数字、数组、对象和函数。子范围原型继承父类parentScope,因此我们有:
如果subscope试图访问parentScope中定义的属性,JavaScript将首先在subScope中查找它,如果它没有这个属性,它将寻找它的继承范围来获取属性,如果继承的原型对象parentScope都没有这个属性,它将继续在其原型中查找,从原型链向上直到它到达rootScope。因此,以下表达式都得出true:
child scope . a string==' parent string ' child scope . an array[1]==20 child scope . an object . property 1==' parent prop 1 ' child scope . a function()==' parent sout out '假设我们执行以下语句。
儿童镜。尚未查询涩味=' childstring '原型链,但已向childscope添加了一个新属性字符串。新属性隐藏(覆盖)了parentScope中同名的属性。当我们在下面讨论ng-repeat和ng-include时,这个概念非常重要。
假设我们这样做:
儿童镜。anArray [1]=' 22' childscope。查询了ano object . property 1=' child prop 1 '原型链,因为在childscope中找不到对象anarray和anObject。它们在parentScope中找到,并且它们的值被更新。在子范围中没有添加新的属性,也没有创建新的对象。(注意:在JavaScript中,数组和函数都是对象。)
假设我们这样做:
儿童镜。anarray=[100,555] childscope。anobject={name:' mark ',country:' USA'}原型链未被查询,子作用域新增了两个对象属性,隐藏(覆盖)了parentScope中同名对象的属性。
应该总结一下。
如果读取了childScope.propertyX,并且childScope具有propertyX,则不查询原型链。如果设置了childScope.propertyX,将不会查询原型链。在最后一种情况下,
删除儿童范围。anarraycildscope . anarray[1]===22//true我们从childscope中删除了该属性,当我们再次访问该属性时,将查询原型链。删除对象的属性将从原型链中带出属性。
第四,AngularJS的Scope继承并创建了一个新的Scope,原型继承:ng-repeat,ng-include,ng-switch,ng-view,ng-controller,直接用scope: true。Directwith transclude: true创建新的作用域,但不继承:directwith scope: {.}.它创建了一个独立的范围。注意:默认情况下,指令不会创建新的Scope,也就是说,默认参数是scope: false。
Ng-include假设在我们的控制器中,
$ scope.myPrimitive=50$ scope . myobject={ NumBer : 11 };HTML是:
脚本类型=' text/Ng-template ' id='/TPL 1 . html ' input Ng-model=' my primitive '/script div ng-include src=' http : '/TPL 1 . html ' '/div脚本类型=' text/Ng-template ' id='/TPL 2 . html ' input Ng-model=' my object . a number '/脚本div Ng-include src=' http : '/TPL 2 . html ' '/div每个Ng-include生成一个子Scope,每个子Scope
在第一个输入文本框中输入(例如“77”),子范围将获得一个新的myPrimitive属性,覆盖父范围的同名属性。这可能不是你所期望的。
在第二个输入文本框中输入(例如,“99”)不会在子范围中创建新的属性,因为tpl2.html将模型绑定到了一个对象属性,原型继承此时起了作用。n模型搜索对象myObject,并在其父范围中找到它。
如果我们不想将模型从数字基类型更改为对象,我们可以用$parent重写第一个模板:
输入模型=' $ parent.myprimitive '在此文本框中输入(例如,“22”)不会创建新属性。模型绑定到父作用域的属性(因为$parent是子作用域指向其父作用域的属性)。
对于所有作用域(原型继承或非继承),Angular总是通过作用域的$parent、$$childHead和$$childTail属性来记录父子关系(即继承关系),为了简单起见,图中没有显示这些属性。
在缺少表单元素的情况下,另一种方法是在父范围中定义一个函数来修改基本数据类型。由于原型继承,作用域确保可以调用这个函数。例如,
//$作用域。setmyprimitive=function(value){ $ scope。myprimitive=父范围中的值;查看演示
ng-switch的原型继承与ng-include相同。因此,如果需要双向绑定基本类型数据,请使用$parent,或者将其更改为对象对象,并将其绑定到对象的属性,以防止子作用域覆盖父作用域的属性。Ng-repeatng-repeat有点不同。假设在我们的控制器中:
$ scope . myarrayofprimities=[11,22];$ scope . myarrayofobjects=[{ num 3360 101 },{ num 3360 202 }]和HTML:
ul Li ng-repeat=' num in myarrayofprimities ' input ng-model=' num '/Liu lul Li ng-repeat=' obj in myArrayOfObjects ' input ng-model=' obj . num '/Liul对于每个项,ng-repeat创建一个新的作用域,每个作用域继承父作用域,但同时,该项的值被分配给新作用域的新属性(新属性的名称是一个循环变量名)。Angular ng-repeat的源代码实际上是这样的:
childScope=作用域。$ new();//子范围原型继承父范围.child scope[value ident]=value;//创建一个新的childScope属性。如果item是一个基本数据类型(就像myArrayOfPrimitives一样),它的值本质上是复制并分配给新的子作用域属性。更改此子范围的属性值(例如,使用ng-model,即num)不会更改父范围引用的数组。因此,上面第一个ng-repeat中每个子作用域获得的num属性独立于myArrayOfPrimitives数组:
这个ng-repeat和你预期的不一样。在Angular 1.0.2和更低版本中,在文本框中输入值将更改灰色网格,该网格仅在“范围”中可见。Angular 1.0.3之后,输入文本将不再有任何效果。我们认为输入可以改变myArrayOfPrimitives数组,而不是子范围中的属性。因此,我们必须将模型更改为对象数组。
因此,如果项是一个对象,对原始对象(而不是范围)的引用将被分配给新的子范围属性。更改子范围属性的值(使用ng-model,即obj.num)也会更改父范围引用的对象。因此,上述第二个ng-repeat可以表示为:
这就是我们想要的。进入文本框将改变灰色网格的值,这在父范围和子范围中都是可见的。用ng-controller嵌套ng-controller的结果是正常的原型继承,如ng-include和ng-switch。所以做法是一样的,不再重复。然而,“两个控制器通过使用$scope继承来共享信息被认为是一种不好的做法”。应该使用服务在控制器之间共享数据。
如果你真的想通过继承来共享数据,那么没有什么特别要做的,子作用域可以直接访问父作用域的所有属性。指令应该在不同的情况下讨论。
default scope : false指令不会创建新的作用域,因此没有原型继承。这看起来很简单,但也很危险,因为您会认为该指令在范围中创建了一个新属性,但实际上它只使用了一个现有属性。这对于编写可重用的模块和组件来说并不好。作用域: true此时,指令将创建一个新的子作用域并继承父作用域。如果同一个DOM节点上的多个指令想要创建一个新的作用域,将只创建一个新的作用域。因为有正常的原型继承,像ng-include和ng-switch,要注意基本类型数据的双向绑定,子Scope的属性会覆盖同名父Scope的属性。范围:}此时,指令创建一个没有原型继承的独立范围。在编写可重用模块和组件时,这是一个更好的选择,因为指令不会意外读写父范围。然而,有时这样的指令经常需要访问父范围的属性。对象哈希用于在该独立作用域及其父作用域之间建立双向绑定(使用“=”)或单向绑定(使用“@”)。还有一个用于绑定父范围的表达式。所有这些都是从父范围派生的,以创建本地范围属性。请注意,HTML属性用于建立绑定。您不能在对象的哈希中引用父范围的属性名。您必须使用一个HTML属性。例如,div my-directional和scope : { local prop 3360 ' @ parentProp ' }无法将父属性parent prop绑定到独立的作用域,因此必须指定div my-directional the-parent-prop=parent prop和scope : { local prop 3360 ' @ parent prop ' }。独立作用域中的__proto__指的是一个Scope Object(下图中的橙色对象),独立作用域的$parent指向父作用域,所以虽然它是独立的,不继承父作用域原型,但它仍然是一个子作用域。
在下图中,我们有my-directional interpoled=' { { parent prop 1 } } '双向绑定=' parentprop2 '和scope:
{插值参数: ' @插值',双向绑定参数: '=双向绑定' } .同时,假设指令已经完成范围。someisolateprop='我被隔离'在其链接函数中。
注意:在链接功能中,使用attrs。$ observe ('attr _ name ',function(value){ 0.}获取由“@”符号替换的独立作用域的属性值。例如,在链接函数中,有attrs。$ observe(' interpoled '),函数值(value){ 0.}将设置为11。(在链接函数中未定义scope .插值drop。相反,在链接函数中定义了scope.twowayBindingProp,因为使用了符号“=”。)transclude : true此时,指令创建了一个新的“trans clude”子范围,并继承了父范围。因此,如果模板片段中的内容(例如将替换ng-transclude的内容)需要双向绑定父作用域的基本类型数据,请使用$parent,或者对对象属性建模,以防止子作用域属性覆盖父作用域属性。
透明的和独立的作用域(如果有的话)是兄弟,每个作用域的$父作用域指向同一个父作用域。当模板中同时存在作用域和独立作用域时,独立作用域属性$$nextSibling将指向模板中的作用域。在下图中,假设指令与上图相同,但transclude:为真。
在线查看DEMO,示例中有一个showScope()函数,可以用来检查独立的作用域及其关联的transcluded作用域。总结起来有四个范围:
普通进行原型继承的范围—— ng-包括,ng-开关,ng-控制器,范围:为真的指令普通原型继承的范围但拷贝赋值—— ng-重复。每个尼日利亚重复的循环都创建新的子范围,并且子范围总是获得新的属性。独立的用scope:隔离范围——指令{.}。它不是原型继承,但'=', '@' 和'' 提供了访问父范围属性的机制transclude:为真实的的超越范围——指令。它也遵循原型继承,但它同时是任何隔离范围的兄弟。对于所有的角度范围总是会通过范围的$家长,$ $孩子头和$$childTail属性记录父-子关系。
PS :示波器和rootscope的区别范围是超文本标记语言和单个控制器之间的桥梁,数据绑定就靠他了rootscope。是各个控制器中范围的桥梁。用rootscope定义的值,可以在各个控制器中使用。下面用实例详细的说明一下1、js代码
电话备份。控制器(' TestCtrl ',['$scope ',' $rootScope ',函数($scope,$rootScope) { $rootScope.name='这是测试;} ]);电话abapp。控制器(' test 111 ctrl ',['$scope ',' $rootScope ',函数($scope,$ rootScope){ $ scope。name=$ rootScope。姓名;} ]);2、html代码
div ng-controller='TestCtrl '我设置了全局变量。strong { { $ root。name } }/strong/div ng-controller=' test 111 ctrl ' 1,获取全局变量strong{{name}}/strongbr 2,获取全局变量strong { { $ root。name } }/strong/div 3,显示结果
我设置了全局变量。这是测试1,获取全局变量。这是测试2,获取全局变量。这是测试由结果可以看出来,$rootScope.name设置的变量,在所有控制器里面都是可以直接用{{$root.name}}来显示的,很强大。那当然也可以赋值给范围。
版权声明:在AngularJS框架中深度探索范围对象的超级教程是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。

















