AngualrJS学习记录-作用域的层级和事件
与DOM模型相似,作用域在绑定页面元素后,便依据元素的层次关系形成了自身的层级关系,而在这些层级关系中,它们还可以通过事件的传播进行数据的通信,只是这种通过事件的数据通信应用的场景非常有限,仅限与父和子之间的作用域通信。
- 作用域的层级
与DOM树状结构类似,作用域也拥有自己的层级,并且与DOM的树状结构相辅相成,它的顶级作用域只有一个,而下面的子级作用域可以创建多个,子级作用域可以继承父级作用域中的全部属性和方法,但子级作用域之间却不能互相访问各自的属性和方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="../js/angular.min.js"></script>
<style>
body{
font-size:12px;
}
ul{
list-style-type: none;
width:408px;
margin:10px 0;
padding:0;
}
ul .ng-scope{
background-color:#eee;
}
ul li{
float: left;
padding:5px 0;
}
ul .bold{
font-weight:bold;
}
ul .school{
float:right;
margin-right:80px;
}
ul li span{
width:52px;
float: left;
padding:0 10px;
}
</style>
</head>
<body ng-app="app" ng-controller="myCtr">
<ul ng-controller="myCtr2">
<li ng-class="{{school}}">{{s_name}}{{c_name}}</li>
<li ng-class="{{bold}}">
<span>序号</span>
<span>姓名</span>
<span>学号</span>
<span>英文</span>
<span>数学</span>
</li>
<li ng-repeat="stu in data">
<span>{{$index + 1}}</span>
<span>{{stu.name}}</span>
<span>{{stu.sex}}</span>
<span>{{stu.english}}</span>
<span>{{stu.maths}}</span>
</li>
</ul>
<ul ng-controller="myCtr3">
<li ng-class="{{school}}">{{s_name}}{{c_name}}</li>
<li ng-class="{{bold}}">
<span>序号</span>
<span>姓名</span>
<span>学号</span>
<span>英文</span>
<span>数学</span>
</li>
<li ng-repeat="stu in data">
<span>{{$index + 1}}</span>
<span>{{stu.name}}</span>
<span>{{stu.sex}}</span>
<span>{{stu.english}}</span>
<span>{{stu.maths}}</span>
</li>
</ul>
</body>
<script>
var app = angular.module('app',[]);
//父级作用域
app.controller('myCtr',['$scope',function($scope){
$scope.s_name = "广州市第四中学";
$scope.bold = 'bold';
$scope.school = 'school';
}]);
//子级作用域
app.controller('myCtr2',['$scope',function($scope){
$scope.c_name = "初一(2)班";
$scope.data = [
{name : '赵东',sex : '男',english : '88',maths : 95},
{name : '张雷',sex : '女',english : '82',maths : 65},
{name : '何小军',sex : '男',english : '78',maths : 78},
{name : '林冬梅',sex : '女',english : '98',maths : 99}
]
}]);
//子级作用域
app.controller('myCtr3',['$scope',function($scope){
$scope.c_name = "初一(4)班";
$scope.data = [
{name : '李晓',sex : '男',english : '78',maths : 86},
{name : '李明',sex : '男',english : '92',maths : 77},
{name : '蒋栋',sex : '男',english : '85',maths : 86},
{name : '林正红',sex : '女',english : '97',maths : 100}
]
}]);
</script>
</body>
</html>
源码分析:
在例子中我们分别定义3个控制器,父级作用域中我们有一些公有属性,是可以被子级作用域访问到,并且能正确输出的,而子级作用域中分别也写上对应的私有属性,我们可以发现它们属性名相同,但互不影响,说明子级作用域中的属性是不可以被同级作用域去访问的。
如果在父级作用域中的$scope
,添加了”s_name”,”bold”,”school”,然后父级作用域又在$rootScope
对象中添加属性和方法,那么子级作用域将首先访问$scope
对象,然后访问$rootScope
对象。
在子级作用域的视图模板中,当页面渲染”s_name”属性值时,首先,它在取值阶段,将在元素本身所属的作用域中寻找是否存在该属性,如果不存在,则继续向上级作用域中查找,如果没有找到,则直接在顶级的$rootScope
对象中查找,确定属性的作用域之后,再进入计算值阶段,计算后,直接将获取的值渲染在页面的元素中。
*每一个作用域都会自动添加一个类别名为”ng-scope”的css样式,因此,可以通过修改该样式,来显示各作用域所控制的范围区域。
作用域事件的传播
在Angular中,作用域间有非常清晰的层次结构关系,类似于DOM树状图形,最顶层的就是rootScope作用域,其余的都是在它基础上进行分支和嵌套。在这种关系下的作用域,它们之间的数据通信变得相对复杂,概括来说,有下列两种方式可以实现作用域的通信。服务(service)
通过在作用域间创建一个单例的服务,由该服务来处理各个作用域间的数据通信,这种方式以后学到了在详细介绍事件(event)
除使用服务外,还可以通过作用域间的事件进行数据通信,而要使用事件,则必须调用Angular中提供的两个方法$broadcasted
和$emitted
,方法$broadcasted
的功能是将事件从父级作用域传播至子级作用域,它的调用格式如下
$broadcast(eventname,data)
其中,参数eventname为定义的事件名称,data为事件传播过程中携带的数据信息。
方法$emitted
的功能是将事件从子级作用域传播至父级作用域,它的调用格式如下“。
$emitted(eventname,data)
各参数的功能与$broadcast
相同。
除了两个传播事件的方法外,还需要通过调用$on
方法,在作用域中监控传播来的事件并获取相应的数据,它的调用格式如下:
$on(eventname,function(event,data){
//接收传播事件的处理代码
})
在上面例子中,eventname为需要监控的传播事件名称,event为事件传播过程中自带的特征,该特征包括下列几个重要的属性:
- event.targetScope : 返回发起传播事件的作用域名称
- evnet.currentScope : 返回正在接收传播事件的作用域名称
- evnet.name : 传播事件的名称
- event.stopPropagation() : 防止事件进行冒泡操作的函数
- event.preventDefault() : 阻止代码事件的发生
- evnet.defaultPrevented : 当执行了preventDefault()方法时,该属性值为true否则为false
而在$on
方法处理传播事件的函数中,另外一个data参数,则为事件在传播过中携带的数据,通过该对象可以在各个监控的作用域中获取传播时的数据,实现数据通信的功能。
虽然说通过作用域的事件可以实现数据通信的功能,但是它们的传播范围非常有限,中能是调用$broadcasted
和$emitted
这两个方法,在父和子级的作用域间进行传播,其他不具有这种关系的作用域将无法监控到传播来的事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="../js/angular.min.js"></script>
<style>
body{
font-size:12px;
}
</style>
</head>
<body ng-app="app">
<div ng-controller="myCtr1">
<div ng-controller="myCtr2">
<button ng-click="to_parent()">向父级作用域传播</button>
<button ng-click="to_child()">向子级作用域传播</button>
<div ng-controller="myCtr3"></div>
</div>
<div ng-controller="myCtr4"></div>
</div>
</body>
<script>
var app = angular.module('app',[]);
//父级作用域
app.controller('myCtr1',['$scope',function($scope){
$scope.$on('event_1',function(event,data){
console.log('在父级中监听到',data);
});
$scope.$on('event_2',function(event,data){
console.log('在父级中监听到',data);
});
}]);
//子级作用域
app.controller('myCtr2',['$scope',function($scope){
$scope.to_parent = function(){
$scope.$emit('event_1','事件来源于子级');
};
$scope.to_child = function(){
$scope.$broadcast('event_2','事件来源于父级');
};
}]);
//子级作用域
app.controller('myCtr3',['$scope',function($scope){
$scope.$on('event_1',function(event,data){
console.log('在子级中监听到',data);
});
$scope.$on('event_2',function(event,data){
console.log('在子级中监听到',data);
});
}]);
//子级作用域
app.controller('myCtr4',['$scope',function($scope){
$scope.$on('event_1',function(event,data){
console.log('在同级中监听到',data);
});
$scope.$on('event_2',function(event,data){
console.log('在同级中监听到',data);
});
}]);
</script>
</body>
</html>
源码分析:
在列子中定义了多个控制器,并通过ng-controller指令将它们与页面中各个作用域相绑定,并且定了2个按钮,分别为”to_parend”和”to_child”。在”to_parend”方法中调用了$emit
方法,向父级作用域传播”event_1”事件和”事件来源于子级”的字符串数据,而在”to_child”中,则调用了$broadcast
方法,向子作用域传播”event_2”和”事件来源于父级”的字符串数据,在其他控制器中则通过调用$on
方法来接收其他作用域传播来的事件和数据。
当点击第一个按钮向父级传播事件后,myCtr1会接收到事件的传播并打印到页面中,而点击第二个按钮向子级传播事件后,myCtr3会接收到事件的传播并打印到页面中。