gab*_*iel 2 javascript loops infinite digest angularjs
我在angularjs中创建了一个自定义过滤器,它按日期对我的元素进行分组.
这是HTML部分:
<table ng-repeat="(index, groupData) in something = (recentTasks | groupBy:dataGroupBy) track by $index">
Run Code Online (Sandbox Code Playgroud)
过滤器如下所示:
module.filter('groupBy', function () {
return function(items, field) {
var groups = [];
switch (field) {
case 'week':
angular.forEach(items, function(item) {
var parsed = parseDateTime(item.date);
var date = new Date(parsed.year, parsed.month - 1, parsed.day);
var back = calculateWeeksBack(date);
if (groups[back] == undefined)
{
groups[back] = {
time_back: calculateWeeksBack(date),
tasks: []
};
}
groups[back].tasks.push(item);
groups[back].total_time += item.time;
});
break;
case 'month':
angular.forEach(items, function(item) {
var parsed = parseDateTime(item.date);
var date = new Date(parsed.year, parsed.month - 1, parsed.day);
var back = calculateMonthsBack(date);
if (groups[back] == undefined)
{
groups[back] = {
time_back: calculateMonthsBack(date),
tasks: []
};
}
groups[back].tasks.push(item);
groups[back].total_time += item.time;
});
break;
default:
angular.forEach(items, function(item) {
var parsed = parseDateTime(item.date);
var date = new Date(parsed.year, parsed.month - 1, parsed.day);
var back = calculateDaysBack(date);
if (groups[back] == undefined)
{
groups[back] = {
time_back: calculateDaysBack(date),
tasks: []
};
}
groups[back].tasks.push(item);
groups[back].total_time += item.time;
});
}
return groups;
}
});
Run Code Online (Sandbox Code Playgroud)
它的作用 - 输入(recentTasks)是一系列任务.每个任务都定义了一个"日期"参数.我需要按日期将这些任务分开 - 每天都在单独的表格中.它有效,但我获得了无限的消化循环.
你能帮我解决一下我的问题吗?或者有更好的解决方案吗?
编辑:输入和输出的例子:
$scope.items = [
{name: 'Abc', date: '2014-03-12'},
{name: 'Def', date: '2014-03-13'},
{name: 'Ghi', date: '2014-03-11'},
{name: 'Jkl', date: '2014-03-12'}
]
Run Code Online (Sandbox Code Playgroud)
输出必须按如下方式分组:
[
'2013-03-11': [
{name: 'Ghi'}
],
'2013-03-12': [
{name: 'Abc'},
{name: 'Jkl'}
],
'2013-03-13': [
{name: 'Def'}
]
]
Run Code Online (Sandbox Code Playgroud)
因为每一天的项目都在HTML结构的分隔表中.
<table ng-repeat="dayData in groupBy(items, 'day')">
<thead>
<tr><td>{{ dayData.date }}</td> </tr>
</thead>
<tbody>
<tr ng-repeat="item in dayData.items">
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
Run Code Online (Sandbox Code Playgroud)
要理解为什么会发生这种情况,您需要了解摘要周期.Angular基于"脏检查",而摘要周期是Angular迭代范围内的所有属性以查看哪些已更改.如果任何属性发生了变化,它会触发所有属性的手表,让他们知道发生了什么事.由于手表可以更改示波器的属性,因此在手表完成后,Angular会进行另一轮脏检查.摘要周期在遍历所有属性时停止,并且它看到它们都没有更改.当手表始终为属性设置新值时,会发生无限摘要.这样的事情可能会更好地解释它:
$scope.myNumber = Math.random();
$scope.$watch('myNumber', function() {
$scope.myNumber = Math.random();
});
Run Code Online (Sandbox Code Playgroud)
也就是说,我们的手表永远不会停止被调用,因为它总是会改变myNumber的值.错误的另一个常见原因是当您有两个属性和两个手表时,如下所示:
$scope.prop1 = Math.random();
$scope.prop2 = Math.random();
$scope.$watch('prop1', function() {
$scope.prop2 = Math.random();
});
$scope.$watch('prop2', function() {
$scope.prop1 = Math.random();
});
Run Code Online (Sandbox Code Playgroud)
这些手表将在无限循环中相互触发.
那么在你的情况下发生的事情是你的过滤器总是返回一个包含新对象的新数组.Angular不会通过检查所有属性并比较它们来比较对象,而是添加一个$$hashkey属性并使用它来与其他对象进行比较.如果两个对象具有相同的$$hashkey属性,则认为它们相等.因此,即使您每次都返回相同的数据结构,Angular也会将其视为一个新对象并运行另一个摘要周期,直到它放弃为止.
因此,为了使您的过滤器工作,您需要更改您的代码,以便对于传入的相同参数,它返回一个具有相同对象的数组,如同对这些对象的相同引用.它应该足以确保您不会总是创建一个新对象,groups[back]而是重用以前的对象.
编辑:
好吧,我会让你失望并说我建议你重新考虑你的过滤器.对于其他开发人员(或者你自己一年后的一年)来说,你有一个ng-repeat不会返回重复列表子集的过滤器,而是一个新的数据结构,这会有点令人困惑.举个例子:
// Controller
$scope.items = [
{name: 'Buy groceries', time: 10},
{name: 'Clean the kitchen', time: 20}
];
// Template
<li ng-repeat="item in items | groupBy:something">
{{item.total_time}}
</li>
Run Code Online (Sandbox Code Playgroud)
有人会看控制器看看item包含什么,他们会看到它有name一个time属性.然后他们查看模板,看到有一个total_time属性,他们不会知道它来自groupBy过滤器,因为Angular中的过滤器通常不会改变对象.
我认为您应该将分组代码提取到作用域上的函数(或创建服务),并仅使用过滤器来过滤掉不相关的项目.
这样,您的模板将会是这样的:
<li ng-repeat="item in groupBy(items, 'something')">
{{item.total_time}}
</li>
Run Code Online (Sandbox Code Playgroud)
这将更加容易混淆,因为很明显该groupBy()方法返回一个新的数据结构.它还将为您修复无限摘要错误.