AngularJS報(bào)錯(cuò)$apply already in progress的解決方法分析
本文實(shí)例分析了AngularJS報(bào)錯(cuò)$apply already in progress的解決方法。分享給大家供大家參考,具體如下:
如果我們使用了AngularJS中的$scope.$apply()或者$scope.$digest(),我們很可能會(huì)遇到類似下面的錯(cuò)誤,雖然這個(gè)錯(cuò)誤沒(méi)有太大影響,但是在日志中看起來(lái)還是很不爽的,日志中記錄的異?;蛘咤e(cuò)誤,就應(yīng)該是需要關(guān)注和解決的問(wèn)題,否則就沒(méi)有必要出現(xiàn)在日志中了。
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)
下面這段代碼就會(huì)報(bào)上面這個(gè)錯(cuò)誤:
var myModule = angular.module('myModule', []); myModule.controller("ctrl_1",function($scope){ $scope.value = "aty"; $scope.$apply(); //$scope.$digest(); });
這個(gè)錯(cuò)誤其實(shí)很好理解:angularJS框架本身已經(jīng)在做臟數(shù)據(jù)檢測(cè)了,我們沒(méi)有必要再手動(dòng)調(diào)用$apply或者$digest。這里自然而然出現(xiàn)了一個(gè)疑問(wèn):什么時(shí)候需要我們手動(dòng)調(diào)用$apply或者$digest,什么時(shí)候不需要呢?這是個(gè)好問(wèn)題,我現(xiàn)在也不知道,目前我只能列出自己在項(xiàng)目中遇到的2種需要手動(dòng)調(diào)用$apply的情況。
情況1:controller中如果有異步操作,比如ajax回調(diào),timeout延時(shí)等。可以這么理解:由于異步(延遲)的存在,當(dāng)開(kāi)始執(zhí)行回調(diào)函數(shù)的時(shí)候,angularJS自身controller中的臟值檢測(cè)已經(jīng)結(jié)束,無(wú)法檢測(cè)到回調(diào)函數(shù)導(dǎo)致數(shù)據(jù)的變化。
<html> <head> <script src="jquery-1.11.1.min.js"></script> <script src="angular.js"></script> <script> 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();//必需手動(dòng)進(jìn)行臟值檢測(cè),否則數(shù)據(jù)無(wú)法刷新到界面 },1000); }); $(function(){ angular.bootstrap($("#div1")[0], ["myModule"]); }) </script> </head> <body> <div id="div1" ng-controller="ctrl_1"> <div>{{text}}</div> <input id="btn" type="button" value="jquery-event"></input> </div> </body> </html>
這段代碼如果沒(méi)有調(diào)用$scope.$apply()的話,數(shù)據(jù)是不會(huì)刷新到界面上的。
情況2:在jQuery代碼中修改$scope中的數(shù)據(jù)。這種情況是在angular框架之外操作$scope中的數(shù)據(jù),angular不能檢測(cè)到數(shù)據(jù)變化是正常的。
<html> <head> <script src="jquery-1.11.1.min.js"></script> <script src="angular.js"></script> <script> 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(); }); }) </script> </head> <body> <div id="div1" ng-controller="ctrl_1"> <div>{{text}}</div> <input id="btn" type="button" value="jquery-event"></input> </div> </body> </html>
在JQuery的事件處理函數(shù)中,我們能夠通過(guò)dom拿到其關(guān)聯(lián)的$scope對(duì)象,進(jìn)而修改$scope中的數(shù)據(jù)。這種情況下,也必需要手動(dòng)調(diào)用$scope.$apply()。
也就是說(shuō)我們必須要清楚哪兒些情況需要手動(dòng)$apply,哪兒些情況不需要手動(dòng)$apply,這看起來(lái)很簡(jiǎn)單,實(shí)際上并不是這樣的??匆欢挝覀冺?xiàng)目中的代碼:
var myModule = angular.module('myModule', []); myModule.controller("ctrl_1",function($scope){ $scope.listItems = []; $scope.loadListFromService = function(){ Spl.MessageProcessor.loadData({ serviceId : "url", data : {}, success : function(json) { $scope.listItems = json.results; // 要不要$scope.$apply()? }, error: function() { console.error("invoke service["+optionsJson.serviceId+"] error."); } }); } $scope.loadListFromService(); });
loadData()這個(gè)函數(shù)很像ajax回調(diào),確實(shí)是這樣的,這個(gè)API不過(guò)是做了一點(diǎn)封裝,大致代碼如下:
function loadData(options) { // 從本地緩存中讀取,很快 var dataInCache = U.loadDataFromCache(options.serviceId); if(dataInCache) { options.success(dataInCache); } else { //異步ajax U.readDataFromServer(options.serviceId, options.data, function(response){ options.success(response); }); } }
由于緩存的影響,$scope.loadListFromService()變得不再那么可控,如果本地已經(jīng)有緩存了,那直接讀取緩存是很快的,這個(gè)時(shí)候不需要手動(dòng)$apply;如果是第一次,本地沒(méi)有緩存,那么就變成了情況1,我們需要手動(dòng)$apply。顯然調(diào)用loadData()函數(shù)的地方,不需要也不應(yīng)該關(guān)注到底有沒(méi)有緩存的存在,這個(gè)時(shí)候判斷需要不要手動(dòng)$apply就沒(méi)有那么容易的。簡(jiǎn)單粗暴的方式,不管怎么樣都手動(dòng)調(diào)用下$scope.$apply(),這樣功能不會(huì)有問(wèn)題,但是日志中就不可避免要出現(xiàn)文中開(kāi)頭提到的錯(cuò)誤。
angular中的$scope中提供了一個(gè)$$phase變量,如果這個(gè)變量的值是"$digest" 或者"$apply",就代表angular自身已經(jīng)在做臟值檢測(cè)了,不需要我們?cè)偃フ{(diào)用$apply或者$digest;否則的話就需要我們手動(dòng)調(diào)用$apply或者$digest了。利用這個(gè)屬性,我們就可以方便解決上面的錯(cuò)誤了,判斷一下唄。下面是一個(gè)工具函數(shù),好理解吧。
function safeApply(scope, fn) { (scope. phase||scope.$root. phase) ? fn() : scope.$apply(fn); }
最后提一下,$digest、$apply、$$phase這些屬性或者方法其實(shí)都是$scope中的私有的,最好不要使用。如果你用到了這些方法,基本可以斷言你的代碼存在問(wèn)題,沒(méi)有按照angular的方式來(lái)組織代碼。比如情況1中的setTimeout,完全可以用angular中的$timeout代替,這才是推薦的方式,而不是通過(guò)$apply來(lái)補(bǔ)救。
<html> <head> <script src="jquery-1.11.1.min.js"></script> <script src="angular.js"></script> <script> var myModule = angular.module('myModule', []); myModule.controller("ctrl_1",function($scope, $timeout){ $scope.text = "place"; $timeout(function(){ $scope.text = "value setted after time out"; },1000); }); $(function(){ angular.bootstrap($("#div1")[0], ["myModule"]); }) </script> </head> <body> <div id="div1" ng-controller="ctrl_1"> <div>{{text}}</div> <input id="btn" type="button" value="jquery-event"></input> </div> </body> </html>
所以解決“$apply already in progress”最好的方式,就是不要使用$scope.$apply()或者$scope.$digest()。
更多關(guān)于AngularJS相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《AngularJS入門(mén)與進(jìn)階教程》及《AngularJS MVC架構(gòu)總結(jié)》
希望本文所述對(duì)大家AngularJS程序設(shè)計(jì)有所幫助。
- angularJS中$apply()方法詳解
- 淺談angular.js中實(shí)現(xiàn)雙向綁定的方法$watch $digest $apply
- AngularJS中的$watch(),$digest()和$apply()區(qū)分
- 全面解析Angular中$Apply()及$Digest()的區(qū)別
- AngularJS中$apply方法和$watch方法用法總結(jié)
- Angular項(xiàng)目中$scope.$apply()方法的使用詳解
- angularjs 中$apply,$digest,$watch詳解
- Angular.js中$apply()和$digest()的深入理解
- AngularJS雙向數(shù)據(jù)綁定原理之$watch、$apply和$digest的應(yīng)用
- Angularjs中的$apply及優(yōu)化使用詳解
相關(guān)文章
Angular 2父子組件數(shù)據(jù)傳遞之@ViewChild獲取子組件詳解
這篇文章主要給大家介紹了關(guān)于Angular 2父子組件數(shù)據(jù)傳遞之@ViewChild獲取子組件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-07-07AngularJS實(shí)現(xiàn)使用路由切換視圖的方法
這篇文章主要介紹了AngularJS實(shí)現(xiàn)使用路由切換視圖的方法,結(jié)合學(xué)生信息管理系統(tǒng)為例分析了使用controllers.js控制器來(lái)切換視圖的具體步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-01-01Angular.JS利用ng-disabled屬性和ng-model實(shí)現(xiàn)禁用button效果
這篇文章主要介紹了Angular.JS利用ng-disabled屬性和ng-model實(shí)現(xiàn)禁用button效果的相關(guān)資料,文中給出了詳細(xì)的示例代碼,相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-04-04將angular.js項(xiàng)目整合到.net mvc中的方法詳解
這篇文章主要給大家介紹了將angular.js項(xiàng)目整合到.net mvc中的相關(guān)資料,文中通過(guò)示例代碼將實(shí)現(xiàn)的過(guò)程介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起看看吧。2017-06-06angular ngClick阻止冒泡使用默認(rèn)行為的方法
這篇文章主要介紹了angular ngClick阻止冒泡使用默認(rèn)行為的方法,較為詳細(xì)的分析了AngularJS中ngClick事件執(zhí)行原理與阻止冒泡的實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-11-11詳細(xì)介紹RxJS在Angular中的應(yīng)用
本篇文章主要介紹了詳細(xì)介紹RxJS在Angular中的應(yīng)用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09AngularJS與后端php的數(shù)據(jù)交互方法
今天小編就為大家分享一篇AngularJS與后端php的數(shù)據(jù)交互方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08基于angular實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)的生日插件
這篇文章主要為大家詳細(xì)介紹了基于angular實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)的生日插件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Angular中使用$watch監(jiān)聽(tīng)object屬性值的變化(詳解)
下面小編就為大家?guī)?lái)一篇Angular中使用$watch監(jiān)聽(tīng)object屬性值的變化(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04