AngularJS中带过滤/未过滤列表使用track by $index的问题
解决单数据源下过滤列表的CRUD索引问题
完全可以在维持单个数组数据源的前提下解决这个问题,给你几个实用的方案:
方案1:用ng-if替代ng-repeat内的filter,保留原数组索引
如果你的数据量不是特别大,这个方案最直接——不用在ng-repeat里过滤,而是遍历整个原数组,用ng-if控制显示符合条件的项。这样每个列表项的$index就是原数组的真实索引,CRUD时直接用它操作$ctrl.items就行:
<!-- 非自定义项列表 --> <md-list-item md-no-ink ng-repeat="item in $ctrl.items track by $index" ng-if="!item.custom"> <!-- 你的列表内容 --> </md-list-item> <!-- 自定义项列表 --> <md-list-item md-no-ink ng-repeat="item in $ctrl.items track by $index" ng-if="item.custom"> <!-- 你的列表内容 --> </md-list-item>
优点:完全不用修改逻辑,直接用原索引操作,CRUD逻辑简单;缺点:会渲染所有数组元素(不符合条件的只是隐藏),数据量大时可能有轻微性能损耗。
方案2:直接操作列表项对象,抛弃$index依赖
既然你用的是对象数组,ng-repeat里的item本身就是原数组元素的引用,根本不需要依赖$index来定位。不管过滤后的列表索引是多少,直接通过item就能找到它在原数组中的位置:
模板代码:
<md-list-item md-no-ink ng-repeat="item in $ctrl.items | filter: {custom: '!!'} track by item.id"> <!-- 显示自定义项内容 --> <button ng-click="$ctrl.deleteCustomItem(item)">删除</button> </md-list-item>
控制器代码:
// 删除自定义项的方法 this.deleteCustomItem = function(item) { const originalIndex = this.items.indexOf(item); if (originalIndex !== -1) { this.items.splice(originalIndex, 1); } };
这里建议把track by $index改成track by item.id(如果item有唯一ID的话),AngularJS能更高效地跟踪列表项,避免重复渲染问题。
优点:不影响过滤逻辑,性能友好,适合大数据量场景;缺点:需要确保item是原数组的引用(对象数组默认都是,基本类型数组的话需要调整)。
方案3:自定义过滤器返回带原索引的对象(可选)
如果一定要保留过滤后的索引同时获取原索引,可以写一个自定义过滤器,把每个过滤后的item包装成包含原索引的对象:
自定义过滤器:
app.filter('withOriginalIndex', function() { return function(items, filterFn) { return items.map((item, index) => ({ item, originalIndex: index })) .filter(wrapped => filterFn(wrapped.item)); }; });
模板使用:
<md-list-item md-no-ink ng-repeat="wrappedItem in $ctrl.items | withOriginalIndex: {custom: '!!'} track by wrappedItem.originalIndex"> <!-- 访问原item:wrappedItem.item --> <!-- 原数组索引:wrappedItem.originalIndex --> <button ng-click="$ctrl.deleteItem(wrappedItem.originalIndex)">删除</button> </md-list-item>
优点:同时拿到过滤后的索引和原索引,灵活性高;缺点:需要额外写过滤器,逻辑稍复杂,一般前两个方案足够解决问题。
内容的提问来源于stack exchange,提问作者Summy
相关产品推荐
相关产品推荐

