1. 程式人生 > >angularJS報錯$apply already in progress

angularJS報錯$apply already in progress

如果我們使用了angularJS中的$scope.$apply()或者$scope.$digest(),我們很可能會遇到類似下面的錯誤:

Error: [$rootScope:inprog] $apply already in progress  
http://errors.angularjs.org/1.3.13/$rootScope/inprog?p0=%24apply  
    at angular.js:63  
    at beginPhase (angular.js:14755)  
    at Scope.$apply (angular.js:14499)  
    at new <anonymous> (1%20-%20%E5%89%AF%E6%9C%AC.html:10)  
    at Object.invoke (angular.js:4185)  
    at extend.instance (angular.js:8454)  
    at angular.js:7700  
    at forEach (angular.js:331)  
    at nodeLinkFn (angular.js:7699)  
    at compositeLinkFn (angular.js:7078)  

下面這段程式碼就會報上面這個錯誤:

var myModule = angular.module('myModule', []);    
myModule.controller("ctrl_1",function($scope){  
    $scope.value = "aty";  
    $scope.$apply();  
    //$scope.$digest();  
});  

這個錯誤其實很好理解:angularJS框架本身已經在做髒資料檢測了,我們沒有必要再手動呼叫$apply或者$digest。這裡自然而然出現了一個疑問:什麼時候需要我們手動呼叫$apply或者$digest,什麼時候不需要呢?

目前我只能列出自己在專案中遇到的2種需要手動呼叫$apply的情況。

情況1:controller中如果有非同步操作,比如ajax回撥,timeout延時,監聽dom事件的回撥函式等。可以這麼理解:由於非同步(延遲)的存在,當開始執行回撥函式的時候,angularJS自身controller中的髒值檢測已經結束,無法檢測到回撥函式導致資料的變化。

var myModule = angular.module('myModule', []);    
myModule.controller("ctrl_1",function($scope){  
    $scope.text = "place";  
              
    setTimeout(function(){  
        $scope.text = "value setted after time out";  
        $scope.$apply();//必需手動進行髒值檢測,否則資料無法重新整理到介面  
    },1000);  


    var w = angular.element($window);
    w.bind('resize', function() {
        $rootScope.$apply();
    }); 
});   

針對這種情況建議使用angular的內建服務$timeout、$http來代替js、jquery原生的函式。

2.在JQuery程式碼中修改$scope中的資料。這種情況是在angular框架之外操作$scope中的資料,angular不能檢測到資料變化是正常的。

var myModule = angular.module('myModule', []);    
myModule.controller("ctrl_1",function($scope){  
    $scope.text = "place";  
});   
          
$(function(){  
    angular.bootstrap($("#div1")[0], ["myModule"]);   
  
    $("#btn").click(function(){  
         var $scope = $("#btn").scope();  
         $scope.text = "value setted in jquery";  
         $scope.$apply();  
    });           
})  

$apply()有兩種形式:

1、接受一個function作為引數,把資料操作放在函式中,執行該function並且觸發一輪迴圈資料檢查(推薦):

$scope.getMessage = function(){
	setTimeout(function(){
		$scope.$apply(function(){
			$scope.message = "......"
		})
	},2000);
}

2、不接受任何引數,只是觸發一輪迴圈:

$scope.getMessage = function(){
	setTimeout(function(){
		$scope.message = "......"
		$scope.$apply();
	},2000);
}