AngularJS中的數據綁定


Data-binding is an automatic way of updating the view whenever the model changes, as well as updating the model whenever the view changes. This is awesome because it eliminates DOM manipulation from the list of things you have to worry about.

在AngularJS中,我們可以只用{{}}表達式或ng-bind``ng-model等指令就能很輕易的實現數據的綁定,而不是用DOM操作。

AngularJS會自動更新scope模型當view視圖發生變化的時候或是更新view當scope發生變化。它很cool,but how does it work?

How it work?

AngularJS數據綁定機制依賴於它的三個擁有強大功能的函數$watch()$digest()$apply()

$watch()

使用$watch(),就可以添加一個監聽器(watcher)。監聽器的作用是監聽值的變化,當值發生變化會收到提示。


	$scope.$watch('myname', function(newValue, oldValue) {
	  //使用newValue 更新DOM
	});

$watch()創建的時候需要指定兩個參數。監控函數監聽函數

  • 上面的表達式myname會被AngularJS解析成監控函數

  • AngularJS會檢查當前監控函數的返回值並且和上一個監控函數的值進行比較,當值發生變化的時候監聽器會被標記為臟的。這就是臟值檢查(dirty-checking)

  • 臟值檢查看似簡單效率低下,但是在語義上始終是正確的

While dirty-checking may seem simple, and even inefficient, it turns out that it is semantically correct all the time

1.1 當我們編寫了一個表達式{{}}ng-model

2.2 AngularJS會在scope上創建一個監聽器,當scope的值發生變化,它會更新視圖。

3.3 同樣我們也可以手動添加監聽器

  • 只監聽必要的變量

  • 盡可能使$watch中的運算簡單,在單個$watch中進行繁雜的運算將使得整個應用變慢


<button ng-click="changeAge()">Change</button>
    <script>
    angular.module('myApp',[])
      .controller("myCtrl",function($scope){
        $scope.myAge = 16;
        $scope.$watch("myAge",function(newValue, oldValue){
        	console.log("My age change! old age:"+oldValue+",new age:"+newValue);
        });
        
        $scope.changeAge = function(){
        	$scope.myAge ++;
        }
        
      });


console:My age change! old age:16,new age:16,My age change! old age:16,new age:17

$digest()

$digest()函數會循環並遍歷其監視的所有scope及其子scope,它會觸發每一個監聽器中的監控函數,如果監聽器是臟的,就會執行對應的監控函數。

  • 當觸發UI事件、ajax請求或是timeout等延時事件修改模型后,AngularJS會自動觸發一次$digest()循環

  • $digest()通常不會只執行一次,當前循環結束后,如果有監控的值變更了,又存在臟東西,則會再運行第二次。只到所有監聽的值不再發生變化。

$apply()

AngularJS並不會直接調用$digest()。它調用通過調用$scope.$apply(),而$scope.$apply()會調用$rootScope.$digest()。因此一個$digest()開始於$rootScope,隨后會訪問所有child scopes並遍歷所有的監聽器。

  • 我們可以執行一些與Angular無關的代碼,這些代碼也還是可以改變scope,$apply可以保證作用域上的監聽器可以檢測這些變更。

  • 如果你更改了AngularJS上下文之外的任何scope,那么你需要手動調用 $apply()來通知更改AngularJS,你正在改變一些模型,它應該觸發檢查。

手動調用$apply()

在上面demo的基礎上我們調用了setTimeout函數,讓myAge++以后,2s后再變為16


<span ng-bind="myAge"></span>
	<button ng-click="changeAge()">Change</button>
    <script>
    angular.module('myApp',[])
      .controller("myCtrl",function($scope){
        $scope.myAge = 16;
        $scope.$watch("myAge",function(newValue, oldValue){
        	console.log("My age change! old age:"+oldValue+",new age:"+newValue);
        });
        
        $scope.changeAge = function(){
        	$scope.myAge ++;
        	
        	setTimeout(function() {  
	          $scope.myAge = 16;
        	  console.log($scope.myAge);
        	}, 2000);  
        }
        
      });

我們可以看到console,

1.1 頁面加載完成后

My age change! old age:16,new age:16

2.2 點擊按鈕后


My age change! old age:16,new age:16
My age change! old age:16,new age:17
16

setTimeout以后並沒有觸發$watch()函數。只是因為我們在AngularJS庫之外的函數修改了scope。AngularJS無法感知,我們需要調用apply()來通知它。

3.3 手動調用apply()


setTimeout(function() {
	          $scope.$apply(function(){
	          	$scope.myAge = 16;
        	    console.log($scope.myAge);
	          });
        	}, 2000);  


4.4 使用AngularJS內置服務$timeout


angular.module('myApp',[])
      .controller("myCtrl",["$scope","$timeout",function($scope,$timeout){
        $scope.myAge = 16;
        $scope.$watch("myAge",function(newValue, oldValue){
        	console.log("My age change! old age:"+oldValue+",new age:"+newValue);
        });
        
        $scope.changeAge = function(){
        	$scope.myAge ++;
        	
        	$timeout(function() {
	          	$scope.myAge = 16;
        	    console.log($scope.myAge);
        	}, 2000);  
        }
        
      }]);

另附一張stackoverflow上的圖

$observe和$watch區別

我們用stackoverflow上的一個例子簡單看一下AngularJS中$observe和$watch區別。

  • $watch用來觀察表達式,可以是function或是String字符串

  • $observe 不同在於它是Attrbutes對象的一個方法,它用於觀察DOM的attribute變化。


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body ng-controller="myCtrl">
	<div ng-app="App" ng-controller="Ctrl" class="app">
		<div d1 attr1="{{prop1}}-12" attr2="prop2" attr3="33" attr4="'a_string'" attr5="a_string" attr6="{{1+aNumber}}"></div>
		<script>
			angular.module("App", []).controller("Ctrl", ["$scope", function($scope) {

				$scope.prop1 = 'scope_prop1';
				$scope.prop2 = 'scope_prop2';
				$scope.aNumber = 44;
				$scope.obj = {
					prop1: "obj_prop1"
				}
				$scope.changeProperties = function() {
					$scope.prop1 = 'scope_prop1_changed';
					$scope.prop2 = 'scope_prop2_changed';
					$scope.aNumber += 1;
				};
				
			}]).directive('d1', function() {
				return {
					compile: function(tElement, tAttrs) {
						console.log('d1-compile:', tAttrs.attr1, tAttrs.attr2, tAttrs.attr3, tAttrs.attr4, tAttrs.attr5, tAttrs.attr6);
						return function link(scope, iElement, iAttrs) {
							console.log('d1-link:', iAttrs.attr1, iAttrs.attr2, iAttrs.attr3, iAttrs.attr4, iAttrs.attr5, tAttrs.attr6);
							console.log("----------------$watch----------------");
							scope.$watch(iAttrs.attr1, function(value) {
								console.log('d1-watch a1:', value);
							});
							scope.$watch(iAttrs.attr2, function(value) {
								console.log('d1-watch a2:', value);
							});
							scope.$watch(iAttrs.attr3, function(value) {
								console.log('d1-watch a3:', value);
							});
							scope.$watch(iAttrs.attr4, function(value) {
								console.log('d1-watch a4:', value);
							});
							scope.$watch(iAttrs.attr5, function(value) {
								console.log('d1-watch a5:', value);
							});
							scope.$watch(iAttrs.attr6, function(value) {
								console.log('d1-watch a6:', value);
							});
						};
					}
				};
			});
		</script>
</body>
</html>


directive('d1', function() {
				return {
					compile: function(tElement, tAttrs) {
						console.log('d1-compile:', tAttrs.attr1, tAttrs.attr2, tAttrs.attr3, tAttrs.attr4, tAttrs.attr5, tAttrs.attr6);
						return function link(scope, iElement, iAttrs) {
							console.log('d1-link:', iAttrs.attr1, iAttrs.attr2, iAttrs.attr3, iAttrs.attr4, iAttrs.attr5, tAttrs.attr6);
							
							console.log("----------------$observe----------------");
							iAttrs.$observe('attr1', function(value) {
								console.log('d1-obsrv a1:', value);
							});
							iAttrs.$observe('attr2', function(value) {
								console.log('d1-obsrv a2:', value);
							});
							iAttrs.$observe('attr3', function(value) {
								console.log('d1-obsrv a3:', value);
							});
							iAttrs.$observe('attr4', function(value) {
								console.log('d1-obsrv a4:', value);
							});
							iAttrs.$observe('attr5', function(value) {
								console.log('d1-obsrv a5:', value);
							});
							iAttrs.$observe('attr6', function(value) {
								console.log('d1-obsrv a6:', value);
							});
						};
					}
				};
			});


directive('d1', function() {
					return {
						scope: {
							isolate_prop1: '@attr1',
							isolate_prop2: '=attr2',
							isolate_prop3: '@attr3',
							isolate_prop4: '@attr4',
							isolate_prop5: '@attr5',
							isolate_prop6: '@attr6'
						},
						compile: function(tElement, tAttrs) {
							console.log('d1-compile tAttrs:', tAttrs.attr1, tAttrs.attr2, tAttrs.attr3, tAttrs.attr4, tAttrs.attr5, tAttrs.attr6);
							return function link(scope, iElement, iAttrs) {
								console.log('d1-link iAttrs:', iAttrs.attr1, iAttrs.attr2, iAttrs.attr3, iAttrs.attr4, iAttrs.attr5, tAttrs.attr6);
								console.log('d1-link isolate:', scope.isolate_prop1, scope.isolate_prop2, scope.isolate_prop3, scope.isolate_prop4, scope.isolate_prop5,scope.isolate_prop6);
								scope.$watch('isolate_prop1', function(value) {
									console.log('d1-watch a1:', value);
								});
								scope.$watch('isolate_prop2', function(value) {
									console.log('d1-watch a2:', value);
								});
								scope.$watch('isolate_prop3', function(value) {
									console.log('d1-watch a3:', value);
								});
								scope.$watch('isolate_prop4', function(value) {
									console.log('d1-watch a4:', value);
								});
								scope.$watch('isolate_prop5', function(value) {
									console.log('d1-watch a5:', value);
								});
								scope.$watch('isolate_prop6', function(value) {
									console.log('d1-watch a6:', value);
								});
							};
						}
					};
				});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM