如何修复Error:[$rootScope:inprog]?动态刷新ng-repeat代码优化咨询
AngularJS 问题解答
1. 如何修复 Error:[$rootScope:inprog] 错误?
这个错误本质是Angular的digest循环正在进行时,你又尝试触发了另一个$apply/$digest操作,导致循环冲突。下面是几种靠谱的解决方式:
- 避免在Angular上下文内调用
$apply:像ng-click、$watch回调、$http响应这些场景,本身就处于Angular的digest循环中,完全不需要再手动调用$scope.$apply()——这是最常见的触发原因。 - 用
$timeout延迟执行:如果你是在Angular上下文之外(比如原生JS事件、setTimeout、第三方库回调)需要更新视图,别直接用$apply,改用$timeout,它会自动在当前digest循环结束后触发新的循环,而且语法更简洁:$timeout(function() { // 这里写你要更新scope的代码,比如$scope.todos = newData; }); - 谨慎使用
$$phase判断(不推荐):虽然可以通过$rootScope.$$phase检查当前是否处于digest循环,但$$phase是Angular内部API,可能在版本更新中变化,应急可以用,但优先选上面的方法:if (!$rootScope.$$phase) { $scope.$apply(); }
2. ng-repeat 动态刷新的代码优化
先直接说你的代码问题:完全没必要在$watch回调里调用$apply,也不需要重新赋值$scope.todos = newVal。因为$watch的回调本身就是在digest循环中执行的,newVal已经是$scope.todos的最新值,你这么做不仅多余,还可能触发上面的[$rootScope:inprog]错误。
下面给你正确的实现思路:
情况1:修改数组/对象的引用(推荐,性能更好)
Angular的$watch默认是引用监听,只要你给$scope.todos赋一个新的数组/对象,ng-repeat就会自动刷新视图,不需要额外操作:
// 添加新项时,创建新数组(而不是修改原数组) $scope.addTodo = function(newTodo) { // 用扩展运算符创建新数组 $scope.todos = [...$scope.todos, newTodo]; // 或者用concat // $scope.todos = $scope.todos.concat([newTodo]); }; // 替换整个数组 $scope.refreshTodos = function(newTodos) { $scope.todos = newTodos; // 直接赋新数组,自动触发刷新 };
情况2:直接修改原数组(比如push/splice)
其实Angular已经对数组的push、pop、splice、shift等方法做了封装,调用这些方法时会自动触发digest循环,ng-repeat会跟着刷新。除非你是在Angular上下文之外修改数组(比如原生click事件、setTimeout),这时候才需要用$timeout或者$apply来触发刷新:
// 正常在Angular上下文内调用push,自动刷新 $scope.addTodo = function(newTodo) { $scope.todos.push(newTodo); }; // 如果是在原生事件里修改,用$timeout document.getElementById('add-btn').addEventListener('click', function() { $timeout(function() { $scope.todos.push({text: 'New Todo'}); }); });
情况3:监听复杂对象的内部变化(尽量避免)
如果todos是嵌套的复杂对象,需要监听内部属性变化,才需要开启$watch的第三个参数true(深度监听),但回调里不需要做任何额外赋值:
// 深度监听todos的内部变化 $scope.$watch('todos', function(newVal) { // 这里可以做一些基于newVal的逻辑,比如统计完成数 $scope.completedCount = newVal.filter(t => t.completed).length; }, true);
注意:深度监听会遍历对象的所有属性,数据量大时会影响性能,尽量用引用变更的方式替代。
内容的提问来源于stack exchange,提问作者mkip
相关产品推荐
相关产品推荐

