在Angular 1.2以后的版本中,对于嵌套Controller的$scope处理就一直是一个很大的坑,这部分主要得益于JavaScript语言级对继承机制(原型继承)。
本文主要分析Angular对于嵌套Controller作用域的另外一种解决方法,使用controller as 语法进行处理。
上篇为$scope的示例分析,下篇为controller as的示例分析。
$scope的传统写法示例
假设我们在有3个Controller进行嵌套,打印出同一个变量,使用$scope的代码是
controller.js
angular.module('app')
.controller('MainCtrl', function ($scope) {
$scope.title = "第一层";
})
.controller('AnotherCtrl', function ($scope) {
$scope.title = "第二层";
})
.controller('YetAnotherCtrl', function ($scope) {
$scope.title = "第三层";
})
与之对应的view代码则为
view.html
<div ng-controller="MainCtrl">
{{ title }}
<div ng-controller="AnotherCtrl">
{{ title }}
<div ng-controller="YetAnotherCtrl">
{{ title }}
</div>
</div>
</div>
则HTML的页面结果就是
<div ng-controller="MainCtrl" class="ng-scope ng-binding">
第一层
<div ng-controller="AnotherCtrl" class="ng-scope ng-binding">
第二层
<div ng-controller="YetAnotherCtrl" class="ng-scope ng-binding">
第三层
</div>
</div>
</div>
$scope的继承机制
继承
如当第三层的YetAnotherCtrl不在自己的$scope内对title变量做赋值操作,则$scope.title会指向上一层的AnotherCtrl中的$scope中进行调用,这里有一个向上寻找的过程,很类似Java中的多态实现(但只是类似)。
故修改controller.js,注释删除掉内部两个ctrl的赋值语句。
controller.js
angular.module('app')
.controller('MainCtrl', function ($scope) {
$scope.title = "第一层";
})
.controller('AnotherCtrl', function ($scope) {
// $scope.title = "第二层";
})
.controller('YetAnotherCtrl', function ($scope) {
// $scope.title = "第三层";
})
则对应的view输出怎么会变为
<div ng-controller="MainCtrl" class="ng-scope ng-binding">
第一层
<div ng-controller="AnotherCtrl" class="ng-scope ng-binding">
第一层
<div ng-controller="YetAnotherCtrl" class="ng-scope ng-binding">
第一层
</div>
</div>
</div>
YetAnotherCtrl与AnotherCtrl因为在自己的$scope中未对title属性做复制,则会一层一层向父类$scope去调用,最后在MainCtrl中发现了title的并进行调用。
调用父类
回到最初如果3个Ctrl分别对title键值做出了赋值,我们需要显式的在第三层中调用第一层的title值需要怎么做呢?
controller.js
angular.module('app')
.controller('MainCtrl', function ($scope) {
$scope.title = "第一层";
})
.controller('AnotherCtrl', function ($scope) {
$scope.title = "第二层";
})
.controller('YetAnotherCtrl', function ($scope) {
$scope.title = "第三层";
})
view.html
<div ng-controller="MainCtrl">
{{ title }}
<div ng-controller="AnotherCtrl">
{{ title }}
{{$parent.title}}
<div ng-controller="YetAnotherCtrl">
{{ title }}
{{$parent.title}}
</div>
</div>
</div>
我们需要通过$parent去直接操作当前$scope的父$scope去显示其内部的值,根据以上代码则会显示如下的html结果
<div ng-controller="MainCtrl" class="ng-scope ng-binding">
第一层
<div ng-controller="AnotherCtrl" class="ng-scope ng-binding">
第二层 第一层
<div ng-controller="YetAnotherCtrl" class="ng-scope ng-binding">
第三层 第二层
</div>
</div>
</div>
终极版
最后,如果我们去掉第二层的title赋值语句将controller.js改成
angular.module('app')
.controller('MainCtrl', function ($scope) {
$scope.title = "第一层";
})
.controller('AnotherCtrl', function ($scope) {
// $scope.title = "第二层";
})
.controller('YetAnotherCtrl', function ($scope) {
$scope.title = "第三层";
})
那么刚才最终获得HTML会变成什么样呢?
答案是
<div ng-controller="MainCtrl" class="ng-scope ng-binding">
第一层
<div ng-controller="AnotherCtrl" class="ng-scope ng-binding">
第一层 第一层
<div ng-controller="YetAnotherCtrl" class="ng-scope ng-binding">
第三层 第一层
</div>
</div>
</div>
主要原理如下
- 当第三层YetAnotherCtrl调用$scope.title的时候,因为YetAnotherCtrl中又对应的操作故最后获取的是自己所赋值的字符串;
- 当第三层YetAnotherCtrl调用$parent.title。则$parent会先去查找Another中的$scope是否有title值,结果是没有的,故会再向上找,找到MainCtrl中的title值。
- 当第二层AnotherCtrl调用$scope.title的时候,同理因为本身没有故会向上查找;
小结
$scope相当于给所有的controller提供了可继承共享的上下文,很灵活,副作用是不好排错、子类调用父类的代码变得非常的不好理解,并且原型继承天生有很多坑。
相关的文章可见官方一篇介绍$scope的专题:
https://github.com/angular/angular.js/wiki/Understan:ding-Scopes
网友评论