简体   繁体   English

NG重复一次绑定和“跟踪”更改

[英]Ng-repeat one-time binding and “track by” change

Our web app uses ngRepeat to display a list of items. 我们的网络应用使用ngRepeat来显示项目列表。 The array and its objects are never changed, but values of the objects inside can be modified by the user. 数组及其对象永远不会更改,但是用户可以修改内部对象的值。

We generate a unique trackId's for each item. 我们为每个项目生成唯一的trackId。 This trackId is updated every time the item's values change. 每当项目的值更改时,此trackId都会更新。

We are also using the one-time binding syntax to reduce the number of watchers on the page (as it can quickly climb to the thousands). 我们还使用一次性绑定语法来减少页面上的观察者数量(因为它可以迅速攀升至数千个)。

However, this combination does not seem to actually work; 但是,这种组合似乎并没有真正起作用。 If the item's trackId changes but the object's reference stays the same, the item is not re-rendered. 如果项目的trackId发生更改,但对象的引用保持不变,则不会重新渲染该项目。

From the angularJS docs: 从angularJS文档中:

Custom Expression: It is possible to use any AngularJS expression to compute the tracking id, for example with a function, or using a property on the collection items. 自定义表达式:可以使用任何AngularJS表达式来计算跟踪ID,例如使用函数或使用收集项目上的属性。 item in items track by item.id is a typical pattern when the items have a unique identifier, eg database id. 当项目具有唯一标识符(例如数据库ID)时,按item.id跟踪的项目中的item是一种典型模式。 In this case the object identity does not matter. 在这种情况下,对象身份无关紧要。 Two objects are considered equivalent as long as their id property is same. 只要两个对象的id属性相同,就认为它们是等效的。 Tracking by unique identifier is the most performant way and should be used whenever possible. 通过唯一标识符进行跟踪是最有效的方式,应尽可能使用它。

If this is the case, why is the item not destroyed and re-created when the trackId is modified? 如果是这种情况,为什么在修改trackId时不销毁并重新创建该项目?

For example: 例如:

$scope.friends = [
    {name:'John', age:25, id: 'John-25'},
    {name:'Mary', age:40, id: 'Mary-40'},
    {name:'Peter', age:85, id: 'Peter-85'}
];

$scope.change = function(i) {
    var f = $scope.friends[i];
    f.age = Math.floor(Math.random() * 100);
    f.id = f.name + '-' + f.age;
};

And

 <div ng-repeat="friend in friends track by friend.id">
    <div ng-bind="'Track id: ' + friend.id"></div>
    <div ng-bind="'Regular binding:' + friend.age"></div>
    <div ng-bind="::'One-time binding:' + friend.age"></div>
</div>

<button ng-click="change(0)">Change John's age</button>
<button ng-click="change(1)">Change Marys's age</button>
<button ng-click="change(2)">Change Peters's age</button>

In this demo I would expect the object to be destroyed when the trackId changes and the element should be re-rendered with the new one-time binding value. 在此演示中,我希望当trackId更改时对象将被销毁,并且应该使用新的一次性绑定值重新渲染元素。

https://plnkr.co/edit/Lklq3ZNDUuggjgwmkoxj?p=preview https://plnkr.co/edit/Lklq3ZNDUuggjgwmkoxj?p=preview

Does anyone have any suggestions on a way to get around this? 有谁对解决这个问题有任何建议吗? We absolutely cannot remove one-time binding for performance reasons. 由于性能原因,我们绝对不能删除一次性绑定。 I have also looked into angular-bind-notifier but that will require every binding in the repeat to be updated since it cannot target specific rows. 我也研究了angular-bind-notifier,但是这将要求更新重复中的每个绑定,因为它不能针对特定的行。

Thanks 谢谢

Keep the id on each friend constant. 保持每个friendid不变。

If this id is truly just an ID for each friend, why would it need to change? 如果此id确实只是每个朋友的ID,为什么需要更改? If you're using that property for something more than an ID, maybe consider adding a new property on the object and keep the ID's constant. 如果您正在使用该属性的东西比一个ID ,也许考虑对象上添加新的属性,并保持ID的常数。

If you don't want to do that, you could always create some helper function that stores a current friend's id-value in a dictionary with id keys. 如果您不想这样做,则总是可以创建一些帮助函数,该函数将当前朋友的id-value在带有id键的字典中。 Then, you can just update or retrieve the value stored in that friend ID by friend.id . 然后,您可以通过friend.id更新或检索存储在该朋友ID中的friend.id

ng-repeat is using $watchCollection internally which is a shallow watch on the array, so changing a property on one of the array items wont cause the ng-repeat to update. ng-repeat内部使用$watchCollection ,这是阵列上的浅表,因此更改阵列项之一上的属性不会导致ng-repeat更新。 The docs are a bit misleading here. 该文档在这里有点误导。

As you guessed you need to change the object reference. 如您所料,您需要更改对象引用。 After toggling the id replace the object with a shallow copy. 切换ID后,将对象替换为浅表副本。 This will will cause $watchCollection to detect the change and ng-repeat will then replace the item due to the id change. 这将导致$watchCollection检测到更改,然后ng-repeat将由于id更改而替换该项目。

$scope.friends = [
    {name:'John', age:25, id: 'John-25'},
    {name:'Mary', age:40, id: 'Mary-40'},
    {name:'Peter', age:85, id: 'Peter-85'}
];

$scope.change = function(i) {
    var f = $scope.friends[i];
    f.age = Math.floor(Math.random() * 100);
    f.id = f.name + '-' + f.age;

    $scope.friends[i] = Object.assign({}, $scope.friends[i]);
};

Updated plunker: https://plnkr.co/edit/s2ztGQ?p=preview 更新的插件: https ://plnkr.co/edit/s2ztGQ ? p = preview

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM