Lig*_*lb1 832 angularjs angularjs-scope angularjs-digest
我发现自从以角度构建应用程序后,我需要手动将页面更新到我的范围.
我知道这样做的唯一方法是$apply()从我的控制器和指令的范围调用.这个问题是它不断向控制台抛出一个错误:
错误:$ digest已在进行中
有谁知道如何避免这个错误或以不同的方式实现相同的事情?
bet*_*ust 658
最近与Angular人就这个话题进行的讨论:出于面向未来的原因,你不应该使用$$phase
当按下"正确"方式时,答案是当前的
$timeout(function() {
// anything you want can go here and will safely be run on the next digest.
})
Run Code Online (Sandbox Code Playgroud)
我最近在编写角度服务时遇到了这个问题,以包装facebook,google和twitter API,这些API在不同程度上都有回调.
这是服务中的一个例子.(为了简洁起见,服务的其余部分 - 设置变量,注入$ timeout等 - 已被取消.)
window.gapi.client.load('oauth2', 'v2', function() {
var request = window.gapi.client.oauth2.userinfo.get();
request.execute(function(response) {
// This happens outside of angular land, so wrap it in a timeout
// with an implied apply and blammo, we're in action.
$timeout(function() {
if(typeof(response['error']) !== 'undefined'){
// If the google api sent us an error, reject the promise.
deferred.reject(response);
}else{
// Resolve the promise with the whole response if ok.
deferred.resolve(response);
}
});
});
});
Run Code Online (Sandbox Code Playgroud)
请注意,$ timeout的delay参数是可选的,如果未设置则默认为0($ timeout调用$ browser.defer,如果未设置延迟,则默认为0)
有点不直观,但这是写Angular的人的答案,所以这对我来说已经足够了!
Lee*_*Lee 656
不要使用这种模式 - 这最终会导致比它解决的更多错误.即使你认为它固定了一些东西,它也没有.
您可以$digest通过检查来检查a 是否已在进行中$scope.$$phase.
if(!$scope.$$phase) {
//$digest or $apply
}
Run Code Online (Sandbox Code Playgroud)
$scope.$$phase将返回"$digest"或"$apply"是否正在$digest或$apply正在进行中.我相信这些状态之间的区别在于$digest它将处理当前范围及其子项的手表,$apply并将处理所有范围的观察者.
对@ dnc253来说,如果你发现自己正在打电话$digest或$apply经常打电话,你可能会做错了.我通常发现当需要更新范围的状态时,我需要消化,因为DOM事件在Angular的范围之外触发.例如,当twitter引导模式变为隐藏时.有时DOM事件在a $digest正在进行时触发,有时则不触发.这就是我使用这张支票的原因.
如果有人知道,我很想知道一个更好的方法.
来自评论:@anddoutoi
- 不要这样做
if (!$scope.$$phase) $scope.$apply(),这意味着你$scope.$apply()在调用堆栈中不够高.
fro*_*sty 322
摘要周期是同步调用.在完成之前,它不会控制浏览器的事件循环.有几种方法可以解决这个问题.解决这个问题最简单的方法是使用内置的$ timeout,第二种方法是使用下划线或lodash(你应该这样),请调用以下内容:
$timeout(function(){
//any code in here will automatically have an apply run afterwards
});
Run Code Online (Sandbox Code Playgroud)
或者如果你有下划线:
_.defer(function(){$scope.$apply();});
Run Code Online (Sandbox Code Playgroud)
我们尝试了几种解决方法,我们讨厌将$ rootScope注入我们的所有控制器,指令甚至一些工厂.所以,到目前为止,$ timeout和_.defer一直是我们的最爱.这些方法成功地告诉angular等待下一个动画循环,这将保证当前范围.$ apply结束.
flo*_*bon 265
这里的许多答案包含很好的建议,但也可能导致混淆.只需用$timeout是不是最好的,也不是正确的解决方案.此外,如果您担心性能或可扩展性,请务必阅读.
$$phase 对框架是私有的,并且有充分的理由.
$timeout(callback)将等待当前的摘要周期(如果有的话)完成,然后执行回调,然后在结束时运行一个完整的$apply.
$timeout(callback, delay, false)将执行相同的操作(在执行回调之前有一个可选的延迟),但$apply如果你没有修改Angular模型($ scope),则不会触发(第三个参数)来保存性能.
$scope.$apply(callback)除其他外,调用,$rootScope.$digest这意味着它将重新删除应用程序及其所有子项的根范围,即使您处于隔离范围内.
$scope.$digest()将简单地将其模型同步到视图,但不会消化其父节点范围,这可以在使用隔离范围(主要来自指令)处理HTML的孤立部分时节省大量性能.$ digest不接受回调:你执行代码,然后消化.
$scope.$evalAsync(callback)已经引入angularjs 1.2,可能会解决你的大部分麻烦.请参阅最后一段以了解更多相关信息.
如果你得到了$digest already in progress error,那么你的架构是错误的:要么你不需要重新删除你的范围,要么你不应该负责(见下文).
当你得到那个错误时,你正在尝试消化你的范围,因为它已经在进行中:因为你不知道你的范围的状态,你不负责处理它的消化.
function editModel() {
$scope.someVar = someVal;
/* Do not apply your scope here since we don't know if that
function is called synchronously from Angular or from an
asynchronous code */
}
// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
// No need to digest
editModel();
}
// Any kind of asynchronous code, for instance a server request
callServer(function() {
/* That code is not watched nor digested by Angular, thus we
can safely $apply it */
$scope.$apply(editModel);
});
Run Code Online (Sandbox Code Playgroud)
如果你知道你正在做什么,并且在一个大型Angular应用程序的一部分中处理一个孤立的小指令,你可能更喜欢$ digest而不是$ apply来保存性能.
任何$ scope都添加了一种新的强大方法:$evalAsync.基本上,如果一个正在发生,它将在当前摘要周期内执行其回调,否则新的摘要周期将开始执行回调.
$scope.$digest如果你真的知道你只需要同步一个孤立的HTML部分(因为$apply如果没有正在进行的话就会触发新的部分),那仍然不如a ,但这是执行函数时的最佳解决方案这你可以不知道它是否会同步或不执行获取潜在缓存的资源之后,例如:有时这需要一个异步调用到服务器,否则,资源将在本地获取同步.
在这些情况下以及您拥有的所有其他情况下,请!$scope.$$phase务必使用$scope.$evalAsync( callback )
lam*_*tor 88
方便的小助手方法来保持这个过程干燥:
function safeApply(scope, fn) {
(scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}
Run Code Online (Sandbox Code Playgroud)
小智 33
我遇到了像CodeMirror这样的第三方脚本和Krpano同样的问题,甚至使用这里提到的safeApply方法也没有为我解决错误.
但是解决它的是使用$ timeout服务(不要忘记先注入它).
因此,像:
$timeout(function() {
// run my code safely here
})
Run Code Online (Sandbox Code Playgroud)
如果在您的代码中使用
这个
也许是因为它在工厂指令的控制器内部或只是需要某种绑定,那么你会做类似的事情:
.factory('myClass', [
'$timeout',
function($timeout) {
var myClass = function() {};
myClass.prototype.surprise = function() {
// Do something suprising! :D
};
myClass.prototype.beAmazing = function() {
// Here 'this' referes to the current instance of myClass
$timeout(angular.bind(this, function() {
// Run my code safely here and this is not undefined but
// the same as outside of this anonymous function
this.surprise();
}));
}
return new myClass();
}]
)
Run Code Online (Sandbox Code Playgroud)
Tre*_*vor 32
见http://docs.angularjs.org/error/$rootScope:inprog
当你有一个调用时出现的问题$apply有时在Angular代码之外异步运行(当应用$ apply时),有时在Angular代码内同步运行(导致$digest already in progress错误).
例如,当您有一个从服务器异步提取项目并缓存它们的库时,可能会发生这种情况.第一次请求项时,将异步检索它,以免阻止代码执行.但是,第二次,该项目已经在缓存中,因此可以同步检索该项目.
防止此错误的方法是确保调用的代码$apply是异步运行的.这可以通过在调用中运行代码来完成$timeout,并将延迟设置为0(这是默认值).但是,在内部调用代码$timeout会消除调用的必要性$apply,因为$ timeout将$digest自行触发另一个循环,这将反过来执行所有必要的更新等.
解
简而言之,而不是这样做:
... your controller code...
$http.get('some/url', function(data){
$scope.$apply(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
Run Code Online (Sandbox Code Playgroud)
做这个:
... your controller code...
$http.get('some/url', function(data){
$timeout(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
Run Code Online (Sandbox Code Playgroud)
只有$apply当你知道运行它的代码时才会调用它将始终在Angular代码之外运行(例如,你对$ apply的调用将发生在由Angular代码之外的代码调用的回调中).
除非有人意识到使用$timeout结束有一些有影响的缺点,否则$apply我不明白为什么你不能总是使用$timeout(零延迟)代替$apply,因为它会做大致相同的事情.
dnc*_*253 28
当您收到此错误时,它基本上意味着它已经在更新您的视图.你真的不需要$apply()在你的控制器内打电话.如果您的视图没有按预期更新,然后在调用后出现此错误$apply(),则很可能意味着您没有正确更新模型.如果你发布一些细节,我们可以找出核心问题.
CMC*_*kai 11
您也可以使用evalAsync.摘要完成后它会运行一段时间!
scope.evalAsync(function(scope){
//use the scope...
});
Run Code Online (Sandbox Code Playgroud)
Sag*_*r M 10
首先,不要这样修复它
if ( ! $scope.$$phase) {
$scope.$apply();
}
Run Code Online (Sandbox Code Playgroud)
它没有意义,因为$ phase只是$ digest循环的布尔标志,因此你的$ apply()有时不会运行.记住这是一个不好的做法.
相反,使用 $timeout
$timeout(function(){
// Any code in here will automatically have an $scope.apply() run afterwards
$scope.myvar = newValue;
// And it just works!
});
Run Code Online (Sandbox Code Playgroud)
如果您使用下划线或lodash,您可以使用defer():
_.defer(function(){
$scope.$apply();
});
Run Code Online (Sandbox Code Playgroud)
如果您使用这种方式,有时您仍会遇到错误(/sf/answers/900136541/).
试试这个:
if(! $rootScope.$root.$$phase) {
...
Run Code Online (Sandbox Code Playgroud)
小智 5
您应该根据上下文使用$ evalAsync或$ timeout.
这是一个很好解释的链接:
http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm
尝试使用
$scope.applyAsync(function() {
// your code
});
Run Code Online (Sandbox Code Playgroud)
代替
if(!$scope.$$phase) {
//$digest or $apply
}
Run Code Online (Sandbox Code Playgroud)
$applyAsync 安排 $apply 的调用在以后发生。这可用于将需要在同一摘要中计算的多个表达式排队。
注意:在 $digest 中,如果当前作用域是 $rootScope,$applyAsync() 才会刷新。这意味着如果您在子作用域上调用 $digest,它不会隐式刷新 $applyAsync() 队列。
例子:
$scope.$applyAsync(function () {
if (!authService.authenticated) {
return;
}
if (vm.file !== null) {
loadService.setState(SignWizardStates.SIGN);
} else {
loadService.setState(SignWizardStates.UPLOAD_FILE);
}
});
Run Code Online (Sandbox Code Playgroud)
参考:
1. AngularJS 1.3 中的 Scope.$applyAsync() 与 Scope.$evalAsync()
| 归档时间: |
|
| 查看次数: |
424662 次 |
| 最近记录: |