如何从控制器中推送到由 ng-repeat 迭代的数组?
How to push to an array that is iterated by ng-repeat from within a controller?
这里是 Angular 我不知道的行为。
假设我在控制器中有一个项目数组,我在视图中重复它们。我在视图中有一个按钮,当我点击它时,一个项目被推入数组。 ng-repeated 列表在视图中更新。到目前为止一切都很好。
但是
如果我只是在控制器内的数组中推送一个新项目(比方说,我设置了一个超时时间),视图中的 ng-repeat 将不会注册数组的更改。
plunker 示例:http://plnkr.co/edit/1mlOpGVOXmnCAa8W5KEx?p=preview
HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="http://apps.bdimg.com/libs/angular.js/1.4.0-beta.4/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<main ng-controller="MainController as ctrl">
<button ng-click="ctrl.addName()">Add an item</button>
<ul>
<li ng-repeat="name in ctrl.names">
{{name.name}}
</li>
</ul>
</main>
</body>
</html>
JS:
angular.module('myApp', [])
.controller('MainController', function(){
var self = this;
self.test = 2;
self.names = [{name: 'example'}];
self.addName = function(){
self.names.push({name: 'new example'});
console.log(self.names)
};
setTimeout(function(){
self.names.push({name: 'another example'});
console.log(self.names);
}, 1000);
});
观察到的行为:单击按钮将触发 addName
函数并将一个项目添加到列表中。 setTimeout
但是会在数组中推送一个项目,但这不会反映在视图中。在检查控制台日志的结果时,我看到显示的项目有一个 $$hashKey
属性,并且对数组的简单推送将导致缺少此 属性 的项目.
问题:在控制器中向数组添加项目的正确方法是什么?我应该使用 $digest 还是 $apply 之类的?
Angular 通过定期执行脏检查来工作,如果任何观察到的模型值发生变化。它在所谓的 $digest
周期中执行此操作。当 Angular 认为有必要时,这些循环会自动 运行,但如果事件发生在 Angular 上下文之外,Angular 将不知道,不会触发 $digest
循环,将不会检测到更改并且不会更新视图。
当你将一个值推送到数组时(在 Angular 上下文之外),你将需要手动 运行 $digest 循环,例如通过将您的函数包装到 $scope.evalAsync()
中,这将触发范围内的脏检查。
更新: 是的,你也可以调用 $scope.$digest()
,但问题是如果 $digest 循环已经 运行ning,你会得到那个不知名的 "$digest() already in progress" 错误。
$scope.evalAsync()
不会遇到这个问题,因为它会异步触发 $digest
循环。更重要的是,如果 $digest
周期已经在进行中,它将尝试在同一个周期中完成工作,并且不会不必要地触发一个新周期。
您还可以看到 Ben Nadel 的 excellent blog post,更详细地解释了所有内容(例如,与使用 $timeout
服务的比较,后者是另一种选择)。
这里是 Angular 我不知道的行为。
假设我在控制器中有一个项目数组,我在视图中重复它们。我在视图中有一个按钮,当我点击它时,一个项目被推入数组。 ng-repeated 列表在视图中更新。到目前为止一切都很好。
但是 如果我只是在控制器内的数组中推送一个新项目(比方说,我设置了一个超时时间),视图中的 ng-repeat 将不会注册数组的更改。
plunker 示例:http://plnkr.co/edit/1mlOpGVOXmnCAa8W5KEx?p=preview
HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="http://apps.bdimg.com/libs/angular.js/1.4.0-beta.4/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<main ng-controller="MainController as ctrl">
<button ng-click="ctrl.addName()">Add an item</button>
<ul>
<li ng-repeat="name in ctrl.names">
{{name.name}}
</li>
</ul>
</main>
</body>
</html>
JS:
angular.module('myApp', [])
.controller('MainController', function(){
var self = this;
self.test = 2;
self.names = [{name: 'example'}];
self.addName = function(){
self.names.push({name: 'new example'});
console.log(self.names)
};
setTimeout(function(){
self.names.push({name: 'another example'});
console.log(self.names);
}, 1000);
});
观察到的行为:单击按钮将触发 addName
函数并将一个项目添加到列表中。 setTimeout
但是会在数组中推送一个项目,但这不会反映在视图中。在检查控制台日志的结果时,我看到显示的项目有一个 $$hashKey
属性,并且对数组的简单推送将导致缺少此 属性 的项目.
问题:在控制器中向数组添加项目的正确方法是什么?我应该使用 $digest 还是 $apply 之类的?
Angular 通过定期执行脏检查来工作,如果任何观察到的模型值发生变化。它在所谓的 $digest
周期中执行此操作。当 Angular 认为有必要时,这些循环会自动 运行,但如果事件发生在 Angular 上下文之外,Angular 将不知道,不会触发 $digest
循环,将不会检测到更改并且不会更新视图。
当你将一个值推送到数组时(在 Angular 上下文之外),你将需要手动 运行 $digest 循环,例如通过将您的函数包装到 $scope.evalAsync()
中,这将触发范围内的脏检查。
更新: 是的,你也可以调用 $scope.$digest()
,但问题是如果 $digest 循环已经 运行ning,你会得到那个不知名的 "$digest() already in progress" 错误。
$scope.evalAsync()
不会遇到这个问题,因为它会异步触发 $digest
循环。更重要的是,如果 $digest
周期已经在进行中,它将尝试在同一个周期中完成工作,并且不会不必要地触发一个新周期。
您还可以看到 Ben Nadel 的 excellent blog post,更详细地解释了所有内容(例如,与使用 $timeout
服务的比较,后者是另一种选择)。