我想这个标题非常清楚我要问的是什么。我创造了这个小提琴:http://jsfiddle.net/Sourabh_/HB7LU/13142/
在小提琴中,我试图复制async
场景。这只是一个示例,但在AJAX调用中,如果我不使用$scope.$apply()
,则列表不会更新。我想知道每次进行AJAX调用更新列表时是否可以安全地使用$scope.$apply()
,还是有其他一些我可以使用的机制?
我编写的代码用于复制场景(与小提琴相同):
HTML
<div ng-controller="MyCtrl">
<li ng-repeat="item in items">
{{item.name}}
</li>
<button ng-click="change()">Change</button>
</div>
JS
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}];
$scope.change = function(){
test(function(testItem){
$scope.items = testItem;
//$scope.$apply();
})
}
function test(callback){
var testItem = [
{name : "mno"},
{name : "pqr"},
{name : "ste"}
];
setTimeout(function(){callback(testItem)},2000);
}
}
答案 0 :(得分:6)
编辑目前尚不清楚OP正在尝试模拟后端通话。即便如此,使用$timeout
服务是一种避免手动调用$scope.$apply
的好方法,也是一种比使用Promise更常用的解决方案(如果你没有调用{{ 1}}通过用Promise包装它们来强制你的更改进入下一个循环并不总是有意义的。
$http
即可使用。
$apply
是原生$timeout
的包装器,具有重要区别:setTimeout
将至少延迟执行,直到下一个$timeout
周期运行。
因此,在没有延迟的情况下,仍然可以将执行延迟到下一个周期。 2000年通过将延迟执行到2000ms后的下一个周期。
因此,这是一个简单的技巧,可以确保您的更改被Angular选中而无需手动调用$digest
(在任何情况下都被视为不安全)
$apply
答案 1 :(得分:5)
如果您要移植API-Rest-Call,请使用promise
中返回的Controller
,而不是在Rest-Call中设置范围。
$http.get('uri')
.success(function(data) {
$scope.items = data
});
避免使用$apply()
。 From the Angular GitHub Repo:
$scope.$apply()
应该尽可能接近async事件绑定 可能的。请勿在整个代码中随意播放。如果你这样做的话
(!$scope.$$phase) $scope.$apply()
这是因为你不够高 在调用堆栈中。
问题:
$apply()
答案 2 :(得分:3)
当在角度摘要循环中未执行代码时,应使用$apply。在正常情况下,我们不需要使用它,但如果我们有一个从jQuery事件处理程序或setTimeout()
之类的方法调用的代码,我们可能必须使用它。即使你有一个从另一个角度函数调用的函数,如watch
或角度事件处理程序,你也不需要使用$ apply(),因为这些脚本在摘要周期中执行。
一种安全的方法是在调用$scope.$apply()之前检查$scope.$$phase
参数
if($scope.$$phase){
$scope.$apply();
}
在您的情况下,您可以使用另一个答案中建议的$ timeout
答案 3 :(得分:2)
每次使用非“角度方式”的东西时,你需要使用$ apply,就像Anzeo讲述$ timeout一样。
例如,如果您使用jQuery的http而不是angular的$ http,则必须添加$ scope。$ apply。
答案 4 :(得分:0)
正如@gruberb在评论中指出的,如果你试图模拟一个REST调用,你最好使用一个承诺而不是$apply
。
为此,您需要使用$q service来创建并返回一个承诺。然后只需调用它并通过在返回的promise上调用then()
方法来处理结果。
function MyCtrl($scope, $q) {
$scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}];
$scope.change = function(){
test().then(function (items) {
$scope.items = items;
$scope.$apply();
});
};
function test() {
var defered = $q.defer();
var testItem = [
{name : "mno"},
{name : "pqr"},
{name : "ste"}
];
setTimeout(function() {
defered.resolve(testItem);
},2000);
return defered.promise;
}
}
答案 5 :(得分:0)
以上所有答案都提供了一些信息,但他们没有回答我的疑问,或者至少我不理解。所以我给自己的。
在角度文档when to use $apply
中非常清楚地说明了这一点$ http或$ timeout或ng-click,ng -.....的回调函数包含$ apply()。因此,当人们说你不做必须使用$ apply()时,你采取棱角分明的做事方式,就是这样。但是,其中一个提到角度事件处理程序的答案也包含在$ apply()中。这不是真的,或者用户意味着只需点击一种事件(再次ng -....)。如果事件在$ http或$ timeout或ng-click之外的rootScope(或任何范围)上广播,例如:来自自定义服务,那么你需要在你的范围内使用$ apply()尽管$ rootScope 。$ broadcast也是有角度的做事方式。在大多数情况下,我们不需要这个,因为应用程序的状态会在发生某些事情时发生变化。即,点击,选择更改等...当我们分别使用ng-click ng-change时,这些是有角度的已经使用$ apply()。使用signalr或socket.io处理服务器端事件,并在编写自定义指令时,只需要更改指令的范围,这是使用$ apply()
非常必要的几个例子。答案 6 :(得分:0)
更好的方法是使用$scope.$applyAsync();
代替$scope.$apply();
避免使用$ scope。$ apply()的原因如下: