Solution
In short, instead of doing this:
... your controller code... $http.get('some/url', function(data){ $scope.$apply(function(){ $scope.mydate = data.mydata; }); }); ... more of your controller code...
do this:
... your controller code... $http.get('some/url', function(data){ $timeout(function(){ $scope.mydate = data.mydata; }); }); ... more of your controller code...
參考:http://stackoverflow.com/questions/12729122/angularjs-prevent-error-digest-already-in-progress-when-calling-scope-apply
$apply()和$digest()在AngularJS中是兩個核心概念,但是有時候它們又讓人困惑。而為了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的。這篇文章旨在解釋$apply()和$digest()是什么,以及在日常的編碼中如何應用它們。
探索$apply()和$digest()
AngularJS提供了一個非常酷的特性叫做雙向數據綁定(Two-way Data Binding),這個特性大大簡化了我們的代碼編寫方式。數據綁定意味着當View中有任何數據發生了變化,那么這個變化也會自動地反饋到scope的數據上,也即意味着scope模型會自動地更新。類似地,當scope模型發生變化時,view中的數據也會更新到最新的值。那么AngularJS是如何做到這一點的呢?當你寫下表達式如{{ aModel }}時,AngularJS在幕后會為你在scope模型上設置一個watcher,它用來在數據發生變化的時候更新view。這里的watcher和你會在AngularJS中設置的watcher是一樣的:
$scope.$watch(‘aModel‘, function(newValue, oldValue) { //update the DOM with newValue });
傳入到$watch()中的第二個參數是一個回調函數,該函數在aModel的值發生變化的時候會被調用。當aModel發生變化的時候,這個回調函數會被調用來更新view這一點不難理解,但是,還存在一個很重要的問題!AngularJS是如何知道什么時候要調用這個回調函數呢?換句話說,AngularJS是如何知曉aModel發生了變化,才調用了對應的回調函數呢?它會周期性的運行一個函數來檢查scope模型中的數據是否發生了變化嗎?好吧,這就是$digest循環的用武之地了。在$digest循環中,watchers會被觸發。當一個watcher被觸發時,AngularJS會檢測scope模型,如何它發生了變化那么關聯到該watcher的回調函數就會被調用。那么,下一個問題就是$digest循環是在什么時候以各種方式開始的?在調用了$scope.$digest()后,$digest循環就開始了。假設你在一個ng-click指令對應的handler函數中更改了scope中的一條數據,此時AngularJS會自動地通過調用$digest()來觸發一輪$digest循環。當$digest循環開始后,它會觸發每個watcher。這些watchers會檢查scope中的當前model值是否和上一次計算得到的model值不同。如果不同,那么對應的回調函數會被執行。調用該函數的結果,就是view中的表達式內容(譯注:諸如{{ aModel }})會被更新。除了ng-click指令,還有一些其它的built-in指令以及服務來讓你更改models(比如ng-model,$timeout等)和自動觸發一次$digest循環。
目前為止還不錯!但是,有一個小問題。在上面的例子中,AngularJS並不直接調用$digest(),而是調用$scope.$apply(),后者會調用$rootScope.$digest()。因此,一輪$digest循環在$rootScope開始,隨后會訪問到所有的children scope中的watchers。