Angular.js 1.4.7 动态 ng-options 导致多个方法调用和 infdig 错误
Angular.js 1.4.7 dynamic ng-options causing mutiple method calls and infdig errors
我有一个 select 列表,其中的内容是根据第一个 select 列表中 selected 的内容动态生成的。我似乎无法找到为什么每次更改第一个 select 列表时都会触发这些错误:
Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"fn:
regularInterceptedExpression","newVal":42,"oldVal":37},{"msg":"fn: regularInterceptedExpression","newVal":"16","oldVal":"14"}],[{"msg":"fn: regularInterceptedExpression","newVal":47,"oldVal":42},{"msg":"fn: regularInterceptedExpression","newVal":"18","oldVal":"16"}],[{"msg":"fn: regularInterceptedExpression","newVal":52,"oldVal":47},{"msg":"fn: regularInterceptedExpression","newVal":"20","oldVal":"18"}],[{"msg":"fn: regularInterceptedExpression","newVal":57,"oldVal":52},{"msg":"fn: regularInterceptedExpression","newVal":"22","oldVal":"20"}],[{"msg":"fn: regularInterceptedExpression","newVal":62,"oldVal":57},{"msg":"fn: regularInterceptedExpression","newVal":"24","oldVal":"22"}]]
http://errors.angularjs.org/1.4.7/$rootScope/infdig?p0=10&p1=%5B%5B%7B%22ms…Expression%22%2C%22newVal%22%3A%2224%22%2C%22oldVal%22%3A%2222%22%7D%5D%5D
此外,每次更改列表时,用于生成第二个列表的 "makeOptions" 每次更改列表都会触发多次。
两个列表设置如下:
<select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options"></select>
<select ng-model="selected.level2" ng-options="opt.value as opt.label for opt in makeOptions()"></select>
控制器是:
app.controller('DemoController', function($scope) {
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.makeOptionsCallCount = 1;
$scope.selected = {};
$scope.makeOptions = function() {
console.log($scope.makeOptionsCallCount++);
var options = [];
for (var i = 0; i < 5; i++) {
options.push({ label: 'Value = ' + i + $scope.selected.level1, value: i + $scope.selected.level1 });
}
return options;
};
});
这是一个奇怪的例子:
http://plnkr.co/edit/mKv7nMjot5XqBj4mkvhR?p=preview
有没有我哪里出错的想法?
更新
我可能过度简化了遇到问题的示例。解决方案变得更加复杂,因为我有一个项目列表,您可以为每个项目选择一组值。很抱歉,我在问我原来的问题时忽略了这个要素。
我创建了更准确反映这一点的新示例:
当您在视图中使用函数时,它会在每个摘要循环中执行。
还 angular 添加观察者来生成此函数,例如,如果您在每次调用时 return 新数组 - 您将进入无限循环,因为 [] != []
在你的情况下,你还有另一个错误:在视图中调用的函数内部你更改了变量,该变量也显示在视图中。所以对于这个变量也添加了watcher.
您在函数调用中更改值,观察变量已更改,运行 再次消化,运行 函数,更改值,查看变量已更改等等...
因此,对于解决方案,更好的简单保存选项首先更改 select。
var app = angular.module('demoApp', [])
app.controller('DemoController', function($scope) {
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.makeOptionsCallCount = 1;
$scope.selected = {};
$scope.makeOptions = function(lvl) {
$scope.options = [];
console.log(++$scope.makeOptionsCallCount);
for (var i = 0; i < 5; i++) {
$scope.options.push({ label: 'Value = ' + i + lvl, value: i + lvl });
}
};
});
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-app="demoApp" ng-controller="DemoController">
<div>
<h2>Infinite Digest 2</h2>
<select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options" ng-change="makeOptionsCallCount=0;makeOptions(selected.level1)"></select>
<select ng-model="selected.level2" ng-options="opt.value as opt.label for opt in options"></select>
<br/>
<br/>
<div>Level 1: {{selected.level1}}</div>
<div>Level 2: {{selected.level2}}</div>
<div>makeOptions() Called: {{makeOptionsCallCount}} times</div>
</div>
</div>
更新:
在您的评论案例中,您可以利用 ng-repeat
创建自己的范围这一事实,并将在 ng-change 事件上创建的列表选项保存到其中。
此外,您甚至可以使用您的函数 makeOptions
。
到ng-change
你可以传递表达式:
ng-change="optionsLvLs = makeOptions(option.level1)"
在每个选项的 ng-repeat
范围内,将创建 optionsLvLs
不会污染您的 option 对象的更改事件,如果这很重要。
var app = angular.module('demoApp', [])
app.controller('DemoController', function($scope) {
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.selected = {};
$scope.options = [{ name: "A" }];
$scope.makeOptions = function(level1) {
var options = [];
for (var i = 0; i < 5; i++)
{
options.push({ label: 'Value = ' + i + level1, value: i + level1 });
}
return options;
};
$scope.addQuery = function(idx) {
$scope.options.splice(idx + 1, 0, {});
};
$scope.removeQuery = function (idx) {
$scope.options.splice(idx, 1);
};
});
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-controller="DemoController" ng-app="demoApp">
<div>
<h2>Infinite Digest 2</h2>
<div ng-repeat="option in options">
<input type="button" value="+" ng-click="addQuery($index)" />
<input type="button" value="-" ng-click="removeQuery($index)" />
<select ng-model="option.level1" ng-options="lvl.name as lvl.name for lvl in level1options" ng-change="optionsLvLs = makeOptions(option.level1)"></select>
<select ng-model="option.level2" ng-options="opt.value as opt.label for opt in optionsLvLs"></select>
<br/>
<br/>
</div>
</div>
{{ options }}
</div>
进行以下更改将不需要调用函数来动态创建数据:
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.level2options = [0,1,2,3,4];
<select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options"></select>
<select ng-model="selected.level2" ng-options="i+selected.level1 as 'Value = '+i+selected.level1 for i in level2options"></select>
这里是 plunker:http://plnkr.co/edit/GQhnjTpB0sLHoiSnchhn?p=preview
我有一个 select 列表,其中的内容是根据第一个 select 列表中 selected 的内容动态生成的。我似乎无法找到为什么每次更改第一个 select 列表时都会触发这些错误:
Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"fn:
regularInterceptedExpression","newVal":42,"oldVal":37},{"msg":"fn: regularInterceptedExpression","newVal":"16","oldVal":"14"}],[{"msg":"fn: regularInterceptedExpression","newVal":47,"oldVal":42},{"msg":"fn: regularInterceptedExpression","newVal":"18","oldVal":"16"}],[{"msg":"fn: regularInterceptedExpression","newVal":52,"oldVal":47},{"msg":"fn: regularInterceptedExpression","newVal":"20","oldVal":"18"}],[{"msg":"fn: regularInterceptedExpression","newVal":57,"oldVal":52},{"msg":"fn: regularInterceptedExpression","newVal":"22","oldVal":"20"}],[{"msg":"fn: regularInterceptedExpression","newVal":62,"oldVal":57},{"msg":"fn: regularInterceptedExpression","newVal":"24","oldVal":"22"}]]
http://errors.angularjs.org/1.4.7/$rootScope/infdig?p0=10&p1=%5B%5B%7B%22ms…Expression%22%2C%22newVal%22%3A%2224%22%2C%22oldVal%22%3A%2222%22%7D%5D%5D
此外,每次更改列表时,用于生成第二个列表的 "makeOptions" 每次更改列表都会触发多次。
两个列表设置如下:
<select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options"></select>
<select ng-model="selected.level2" ng-options="opt.value as opt.label for opt in makeOptions()"></select>
控制器是:
app.controller('DemoController', function($scope) {
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.makeOptionsCallCount = 1;
$scope.selected = {};
$scope.makeOptions = function() {
console.log($scope.makeOptionsCallCount++);
var options = [];
for (var i = 0; i < 5; i++) {
options.push({ label: 'Value = ' + i + $scope.selected.level1, value: i + $scope.selected.level1 });
}
return options;
};
});
这是一个奇怪的例子:
http://plnkr.co/edit/mKv7nMjot5XqBj4mkvhR?p=preview
有没有我哪里出错的想法?
更新
我可能过度简化了遇到问题的示例。解决方案变得更加复杂,因为我有一个项目列表,您可以为每个项目选择一组值。很抱歉,我在问我原来的问题时忽略了这个要素。
我创建了更准确反映这一点的新示例:
当您在视图中使用函数时,它会在每个摘要循环中执行。
还 angular 添加观察者来生成此函数,例如,如果您在每次调用时 return 新数组 - 您将进入无限循环,因为 [] != []
在你的情况下,你还有另一个错误:在视图中调用的函数内部你更改了变量,该变量也显示在视图中。所以对于这个变量也添加了watcher.
您在函数调用中更改值,观察变量已更改,运行 再次消化,运行 函数,更改值,查看变量已更改等等...
因此,对于解决方案,更好的简单保存选项首先更改 select。
var app = angular.module('demoApp', [])
app.controller('DemoController', function($scope) {
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.makeOptionsCallCount = 1;
$scope.selected = {};
$scope.makeOptions = function(lvl) {
$scope.options = [];
console.log(++$scope.makeOptionsCallCount);
for (var i = 0; i < 5; i++) {
$scope.options.push({ label: 'Value = ' + i + lvl, value: i + lvl });
}
};
});
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-app="demoApp" ng-controller="DemoController">
<div>
<h2>Infinite Digest 2</h2>
<select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options" ng-change="makeOptionsCallCount=0;makeOptions(selected.level1)"></select>
<select ng-model="selected.level2" ng-options="opt.value as opt.label for opt in options"></select>
<br/>
<br/>
<div>Level 1: {{selected.level1}}</div>
<div>Level 2: {{selected.level2}}</div>
<div>makeOptions() Called: {{makeOptionsCallCount}} times</div>
</div>
</div>
更新:
在您的评论案例中,您可以利用 ng-repeat
创建自己的范围这一事实,并将在 ng-change 事件上创建的列表选项保存到其中。
此外,您甚至可以使用您的函数 makeOptions
。
到ng-change
你可以传递表达式:
ng-change="optionsLvLs = makeOptions(option.level1)"
在每个选项的 ng-repeat
范围内,将创建 optionsLvLs
不会污染您的 option 对象的更改事件,如果这很重要。
var app = angular.module('demoApp', [])
app.controller('DemoController', function($scope) {
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.selected = {};
$scope.options = [{ name: "A" }];
$scope.makeOptions = function(level1) {
var options = [];
for (var i = 0; i < 5; i++)
{
options.push({ label: 'Value = ' + i + level1, value: i + level1 });
}
return options;
};
$scope.addQuery = function(idx) {
$scope.options.splice(idx + 1, 0, {});
};
$scope.removeQuery = function (idx) {
$scope.options.splice(idx, 1);
};
});
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-controller="DemoController" ng-app="demoApp">
<div>
<h2>Infinite Digest 2</h2>
<div ng-repeat="option in options">
<input type="button" value="+" ng-click="addQuery($index)" />
<input type="button" value="-" ng-click="removeQuery($index)" />
<select ng-model="option.level1" ng-options="lvl.name as lvl.name for lvl in level1options" ng-change="optionsLvLs = makeOptions(option.level1)"></select>
<select ng-model="option.level2" ng-options="opt.value as opt.label for opt in optionsLvLs"></select>
<br/>
<br/>
</div>
</div>
{{ options }}
</div>
进行以下更改将不需要调用函数来动态创建数据:
$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.level2options = [0,1,2,3,4];
<select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options"></select>
<select ng-model="selected.level2" ng-options="i+selected.level1 as 'Value = '+i+selected.level1 for i in level2options"></select>
这里是 plunker:http://plnkr.co/edit/GQhnjTpB0sLHoiSnchhn?p=preview