显然我还不了解ng-repeat
,$$hashKeys
和track by
背后的机制。
我目前正在项目中使用AngularJS 1.6。
问题:
我有一组复杂的对象,我想用它来渲染视图中的列表。但要获得所需的结果,我需要首先修改(或映射/增强/更改)这些对象:
const sourceArray = [{id: 1, name: 'Dave'}, {id:2, name: Steve}]
const persons = sourceArray.map((e) => ({enhancedName: e.name + e.id}))
//Thus the content of persons is:
//[{enhancedName: 'Dave_1'}, {enhancedName: 'Steve_2'}]
将此绑定到视图应该像这样工作:
<div ng-repeat="person in ctrl.getPersons()">
{{person.enhancedName}}
</div>
然而,这显然会遇到$digest()
- 循环,因为.map
每次调用时都会返回新的对象实例。由于我通过函数将其绑定到ng-repeat,因此会在每个$digest
中对其进行重新评估,模型不会稳定并且Angular会不断重新运行$digest
- 周期,因为这些对象被标记为$dirty
为什么我感到困惑
现在这不是一个新问题,有几个解决方案:
在an Angular-Issue from 2012中,Igor Minar自己建议手动设置$$ hashKey-Property以告诉angular生成的对象是相同的。 This是他的工作小提琴,但是因为即使这个非常简单的例子在我的项目中使用它时仍然遇到$digest
- 循环,我尝试在小提琴中升级Angular-Version。出于某种原因it crashes。
好的......从Angular 1.3开始,我们track by
基本上可以解决这个问题。但无论如何
<div ng-repeat="person in ctrl.getPersons() track by $index">
和
<div ng-repeat="person in ctrl.getPersons() track by person.enhancedName">
使用$digest
循环崩溃。我的印象是track by
语句应该让angular认为它适用于相同的对象,但显然情况并非如此,因为它只是不断检查它们是否有变化。说实话,我不知道如何正确调试原因。
问题:
是否可以使用过滤/修改过的数组作为ng-repeat的数据源?
我不想将修改后的数组存储在我的控制器上,因为我需要不断更新其数据,然后必须在控制器中手动维护和刷新它,而不是依赖于数据绑定。
答案 0 :(得分:1)
你提供的“it crashes”小提琴,对我来说并没有产生无限的消化。实际上:它甚至没有成功引导Angular应用程序(看起来像在最新的Angular中无法以这种方式进行引导)。
我rewrote it使用我理解的Angular引导机制。它就像你说的那样重现崩溃。
我找到了成功的方法track by stringified JSON。
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script>
angular.module('myApp',[])
.controller('Ctrl', ['$scope', function($scope) {
angular.extend($scope, {
stringify: function(x) { return JSON.stringify(x) },
getList: function() {
return [
{name:'John', age:25},
{name:'Mary', age:28}
];
}
});
}]);
</script>
<div ng-app="myApp">
<div ng-controller="Ctrl">
I have {{getList().length}} friends. They are:
<ul>
<li ng-repeat="friend in getList() track by stringify(friend)">
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
</li>
</ul>
</div>
</div>
即。我们提供跟踪功能stringify()
。也许还有一个Angular内置版。
track by $index
也起作用 - 与你的发现相反。我认为JsFiddle略微破坏了实验*
*以下是轶事。我相信我遇到了一些 JsFiddle本身的问题。例如:我的track by stringify()
示例在我分叉小提琴并在新的浏览上下文中再次尝试相同的代码之后才起作用。我相信,只要我有任何无限摘要:JsFiddle将总是无限消化。似乎有一些状态在以前的运行中挥之不去。所以,我建议你在JsFiddle 中看到的任何内容都失败了,你再试一次新的JsFiddle中的 。
至于为什么你的$$hashKey
技巧导致无限消化 - 我认为Angular不希望$$hashKey
成为一个函数。因此,它可能不会调用您的函数,而是执行分配给$$hashKey
的函数的引用比较。
由于每次调用$$hashKey
时都会为getList()
分配比较器的新实例:引用可能永远不会在后续消化,所以它会继续尝试摘要。
编辑:更新StackOverflow嵌入和JsFiddle以使用HTTPS CDN(以避免与混合内容安全性发生冲突)。
答案 1 :(得分:0)
只要观看的表达式getPersons()
返回新数组,即使使用相同的元素,使用$digest
比较的===
周期也不能停;无论track by
表达式在ngRepeat
(function() {
angular
.module('app', [])
.controller('AppController', AppController)
function AppController($interval) {
// you may have more performant options here
const hashFn = angular.toJson.bind(angular)
// your mapping logic for presentation
const mapFn = (e) => ({
enhancedName: e.name + e.id
})
// initialization of data
let sourceArray = [{
id: 1,
name: 'Dave'
}, {
id: 2,
name: 'Steve'
}]
// initialization of "cache"
let personList = sourceArray.map(mapFn),
lastListHash = hashFn(sourceArray)
Object.defineProperty(this, 'personList', {
get: function() {
const hash = hashFn(sourceArray)
if (hash !== lastListHash) {
personList = sourceArray.map(mapFn)
lastListHash = hash
}
// you need to return **the same** array
// if the source has not been updated
// to make `$digest` cycle happy
return personList
}
})
// test of changes
$interval(() => sourceArray.push({
id: Date.now(),
name: 'a'
}), 1000)
}
})()
之后的呈现节点发挥作用。
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="AppController as ctrl">
There are {{ctrl.personList.length}} persons.
<ul>
<li ng-repeat="person in ctrl.personList track by $index">
[{{$index + 1}}] {{ person.enhancedName }}
</li>
</ul>
</div>
</div>
&#13;
set /A octetA=%%A+%%I, octetB=%%b+%%j, octetC=%%c+%%k, octetD=%%d+%%l
&#13;