簡體   English   中英

Angular.js無限消化循環,具有ng-repeat和過濾器

[英]Angular.js inifinite digest loop with ng-repeat and filter

我在angularjs中創建了一個自定義過濾器,它按日期對我的元素進行分組。

這是HTML部分:

<table ng-repeat="(index, groupData) in something = (recentTasks | groupBy:dataGroupBy) track by $index">

過濾器如下所示:

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;
    }
});

它的作用 - 輸入(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'}
]

輸出必須按如下方式分組:

[
    '2013-03-11': [
        {name: 'Ghi'}
    ],
    '2013-03-12': [
        {name: 'Abc'},
        {name: 'Jkl'}
    ],
    '2013-03-13': [
        {name: 'Def'}
    ]
]

因為每一天的項目都在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>

要理解為什么會發生這種情況,您需要了解摘要周期。 Angular基於“臟檢查”,而摘要周期是Angular迭代范圍內的所有屬性以查看哪些已更改。 如果任何屬性發生了變化,它會觸發所有屬性的手表,讓他們知道發生了什么事。 由於手表可以更改示波器的屬性,因此在手表完成后,Angular會進行另一輪臟檢查。 摘要循環在遍歷所有屬性時停止,並且它看到它們都沒有更改。 當手表始終為屬性設置新值時,會發生無限摘要。 這樣的事情可能會更好地解釋它:

$scope.myNumber = Math.random();
$scope.$watch('myNumber', function() {
  $scope.myNumber = Math.random();
});

也就是說,我們的手表永遠不會停止被調用,因為它總是會改變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();
});

這些手表將在無限循環中相互觸發。

那么在你的情況下發生的事情是你的過濾器總是返回一個包含新對象的新數組。 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>

有人會查看控制器以查看item包含的內容,並且他們將看到它具有nametime屬性。 然后他們查看模板,看到有一個total_time屬性,他們不會知道它來自groupBy過濾器,因為Angular中的過濾器通常不會改變對象。

我認為您應該將分組代碼提取到作用域上的函數(或創建服務),並僅使用過濾器來過濾掉不相關的項目。

這樣,您的模板將會是這樣的:

<li ng-repeat="item in groupBy(items, 'something')">
  {{item.total_time}}
</li>

這將更加容易混淆,因為很明顯groupBy()方法返回一個新的數據結構。 它還將為您修復無限摘要錯誤。

多個源將告訴您不要在ngRepeat中每次調用函數時創建新的可變對象,因為摘要周期將在確定對象是否相同之前檢查對象是否相同,然后每個新調用將創建一個等價但不相同的對象。 正如其他人所說,“正確的解決方案是使用穩定的模型,這意味着將數組分配給范圍/控制器而不是使用吸氣劑。” 在使用嵌套的ngRepeats時遇到了這個問題,這使得在沒有getter的情況下很難做到這一點。

我發現最簡單的解決方案是使用ngInit初始化變量以引用函數調用的結果,然后使用ngRepeat通過該穩定變量。 例如:

<div ng-init="issues=myCtrl.getUniqueIssues()">
    <div ng-repeat="issue in issues">{{issue.name}}</div>
</div>

我的來源也有一個帶過濾器的例子: https//tahsinrahit.wordpress.com/2015/01/04/solution-of-infinite-digest-loop-error-for-ng-repeat-with-object-property -篩選功能於angularjs /

有兩種方法可以解決這個問題:

  1. 對數組的每個元素使用$$ hashKey屬性。 Angular不會通過檢查所有屬性並比較它們來比較對象,而是添加$$ hashkey屬性並使用它來與其他對象進行比較。 如果兩個對象具有相同的$$ hashkey屬性,則認為它們相等。 因此,即使您每次都返回相同的數據結構,Angular也會將其視為一個新對象並運行另一個摘要周期,直到它放棄為止。 見:

    • 與$ digest問題的老小提琴 - jsfiddle.net/LE6Ay

    • $$ hashKey屬性已添加 - jsfiddle.net/LE6Ay/1

  2. 不要在每個getList()調用上創建新對象。 正確的解決方案是使用穩定模型,這意味着將數組分配給范圍/控制器而不是使用getter。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM