angularJs中的作用域($scope)結構和DOM結構非常相似,也是一個層次分明的樹狀結構。它有一個根作用域$rootscope(對應angular應用或ng-app),其他作用域是嵌套在根作用域下面的。
angularJs的$scope之間都遵循js中對象原型繼承方式,當子作用域中沒有該對象時,默認向上級作用域(父作用域)尋找,直到找到或者到達$rootscope爲止,當子作用域有該對象時,使用子作用域中的對象。
注:這裏的父作用域包含直接父級和祖先,子作用域包含直接子級和更下層級
例子如下:
<body ng-app="myApp">
<div ng-controller="fatherCtrl">
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>Father: {{name}}!</h1>
<div ng-controller="sonCtrl">
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>Son: {{name}}</h1>
</div>
</div>
</body>
其中fatherCtrl 和 sonCtrl 裏都使用了 name,但sonCtrl中並沒有定義name。
var app = angular.module ('myApp', []);
app.controller('fatherCtrl', function ($scope) {
$scope.name = "father";
$scope.$watch("name",function () {
console.log("fatherScope:"+$scope.name)
})
});
app.controller('sonCtrl', function ($scope) {
$scope.$watch("name",function () {
console.log("sonScope:"+$scope.name)
})
});
這時sonCtrl 直接讀取 fatherCtrl 中的 name,改變fatherCtrl中name的值,sonCtrl顯示的值也會同步改變。
效果如圖:
但當通過view同步改變sonCtrl中name的值時,sonCtrl中會重新創建一個name的對象,faterCtrl中的值不會改變。
效果如圖:
如何在作用域之間通信呢?
1.創建一個單例服務,然後通過這個服務處理所有子作用域的通信。
2.通過作用域中的事件處理通信。但是這種方法有一些限制;你並不能廣泛的將事件傳播到所有監控的作用域中。你必須選擇是與父級作用域或者子作用域通信。
$on、$emit和$broadcast使得event、data在controller之間的傳遞變的簡單。
1、$emit:子傳父,傳遞event與data;
$scope.$emit('name', 'args');
2、$broadcast:父傳子,傳遞event與data;
$scope.$broadcast('name', 'args');
3、$on:監聽或接收數據,用於接收event與data;
$scope.$on('name', function(event,data){});
$broadcast、$emit事件必須依靠其他事件(ng-click等)進行觸發。
值得注意的是:以上事件的主語是 $scope, 因爲所有的事件其實都是作用在scope上的。
在$on的方法中的event事件參數,其對象的屬性和方法如下:
事件屬性/方法 | 功能性說明 |
event.targetScope | 獲取傳播事件的作用域 |
event.currentScope | 獲取接收事件的作用域 |
event.name | 傳播的事件的名稱 |
event.stopPropagation() | 阻止事件進行冒泡傳播,僅在$emit事件中有效 |
event.preventDefault() | 阻止默認事件的發生 |
event.defaultPrevented | 如果調用了preventDefault事件則返回true |
事件傳播的方向如下圖:
1、$emit
該服務貫穿作用域發出一個向上的事件,該事件的生命週期開始於emit被啓動的地方,事件一直朝着根作用域傳遞,傳遞期間會通知那些註冊在作用域上的監聽器,在這期間,作用域中的監聽器接收到通知,獲取事件,但是不會註銷事件,事件繼續往下傳播。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="fatherCtrl">
<!--fatherCtrl可以得到-->
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>Father: {{name}}!</h1>
<div ng-controller="sonCtrl">
<!--sonCtrl可以得到-->
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>Son: {{name}}</h1>
<div ng-controller="innerCtrl">
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>inner: {{name}}</h1>
</div>
</div>
<div ng-controller="broCtrl">
<!--broCtrl得不到-->
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>brother: {{name}}</h1>
</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('fatherCtrl', function ($scope) {
$scope.name = "father";
$scope.$on('test', function (e, newName) {
$scope.name = newName;
});
});
app.controller('sonCtrl', function ($scope) {
$scope.name = "son";
$scope.$on('test', function (e, newName) {
$scope.name = newName;
});
});
app.controller('broCtrl', function ($scope) {
$scope.name = "brother";
$scope.$on('test', function (e, newName) {
$scope.name = newName;
});
});
app.controller('innerCtrl', function ($scope) {
$scope.name = "inner";
$scope.nameOnChange = function () {
$scope.$emit('test', $scope.name);
}
});
</script>
</body>
</html>
效果如下:
2、$broadcast
該服務發佈一個向下的事件給作用域中的所有子節點,該事件的生命週期也是從broadcast被啓動開始。下面的所有子作用域都會接收到通知。之後,事件向下傳播,在這期間,作用域中的監聽器接收到通知,獲取事件,但是不會註銷事件,事件繼續往下傳播。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="fatherCtrl">
<!--fatherCtrl得不到-->
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>Father: {{name}}!</h1>
<div ng-controller="sonCtrl">
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>Son: {{name}}</h1>
<div ng-controller="outCtrl">
<!--outCtrl可以得到-->
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>out in son: {{name}}</h1>
<div ng-controller="innerCtrl">
<!--innerCtrl可以得到-->
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>inner in out: {{name}}</h1>
</div>
</div>
</div>
<div ng-controller="broCtrl">
<!--broCtrl得不到-->
<input type="text" ng-model="name" ng-change="nameOnChange()">
<h1>brother: {{name}}</h1>
</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('fatherCtrl', function ($scope) {
$scope.name = "father";
$scope.$on('test', function (e, newName) {
$scope.name = newName;
});
});
app.controller('sonCtrl', function ($scope) {
$scope.name = "son";
$scope.nameOnChange = function () {
$scope.$broadcast('test', $scope.name);
}
});
app.controller('broCtrl', function ($scope) {
$scope.name = "brother";
$scope.$on('test', function (e, newName) {
$scope.name = newName;
});
});
app.controller('innerCtrl', function ($scope) {
$scope.name = "inner";
$scope.$on('test', function (e, newName) {
$scope.name = newName;
});
});
app.controller('outCtrl', function ($scope) {
$scope.name = "out";
$scope.$on('test', function (e, newName) {
$scope.name = newName;
});
});
</script>
</body>
</html>
效果如圖:
3、$on
該服務監聽指定類型的事件,獲取從emit或者broadcast發佈的事件。
注:
1、如果在作用域中沒有父子關係存在,可以在控制器中注入$rootScope、使用$broadcast服務向下傳播事件(但這個慎用),但是不能通過$emit向上傳播事件。
2、在作用域中存在父子關係時,可以也僅可以由子控制器使用$emit服務向上傳播事件,同時父作用域中的控制監聽器可以註銷事件。
————————————————
版權聲明:本文爲CSDN博主「冰雪_318」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/lhj20084720208/article/details/78997015