AngularJs - ngRepeat 带有返回新对象的过滤器 [英] AngularJs - ngRepeat with a filter that returns a new object
问题描述
我正在尝试对过滤器中的对象应用转换,这会导致返回一组新对象.这是因为我想在应用转换后过滤对象并显示转换结果.但是,我最终得到了一个无限摘要,因为我显示的对象与我放入的对象不同(比较它们的 $$ids
时).我解决这个问题的想法如下:
使用类似
track by item.id
的跟踪表达式,并将原始对象的ids
分配给每个转换后的对象.虽然我所有的对象目前都有一个id
,但这似乎是一个坏主意,因为它使过滤器变得不那么通用——原始对象必须有一个id
,转换必须不设置id
(因为它会被覆盖)等将原始对象的
$$id
分配给转换后的对象.根据我的理解,$$id
应该是只读的.根据转换的过滤结果返回原始对象的子集.这可能会导致性能问题,因为需要在过滤器和显示表达式中应用转换,而且我必须循环返回已转换/已过滤的项目以选择要返回的正确原始项目.
这里是过滤器:
listModule.filter('ui.filter.transformFilter',['$过滤器','$id',功能($过滤器,$ id){var Filter = $filter('filter');返回函数(对象,转换器,表达式){//先决条件——我们需要一个对象列表如果(!_.isArray(对象)){返回对象;}var 转换 = [];for (var i = 0; i
这是我尝试使用它的方式:
<tr ng-repeat="item in list.items | ui.filter.transformFilter:list.transformerFunction:list.search" ng-click="list.select({'item': item})" class="list-item"><td ng-repeat="list.labels 中的标签" ng-bind-html="item[label.key]"></td></tr>
哦,理想情况下 ngClick
返回原始对象,但我总是可以在它周围包裹一个函数来查找它.
这个问题的一个解决方案是,当您有一个幂等函数时,Angular 由于对象 ID 而认为不是幂等的(因此导致 $digest 循环问题为你注意到)是使用 lo-dash/underscore 的 _.memoize
来缓存你的函数的结果.
这将保证对于任何给定的缓存键,您的过滤器将始终返回完全相同的对象(包括 $$id
).这样您就不必使用 $$id
玩游戏,而且您可以获得 不必在每个 $digest 循环上重新计算过滤结果的性能优势.>
以下是缓存过滤器结果的方法:
return _.memoize(function(objects, Transformer, expression) { ... },函数(对象,转换器,表达式){返回对象 +transformer.name + 表达式;});
针对您的情况的一个重要说明是,默认情况下 _.memoize
使用第一个函数参数(在本例中为 objects
)作为缓存键.由于给定不同的转换器函数和表达式,您的过滤器可能会产生不同的结果,因此我添加了可选的第二个参数 - 一个使用 objects
、expression
和名称的哈希函数transformer
函数产生一个缓存键.
这是使用此代码的简化版本:fiddle
I'm trying to apply a transformation to the objects in my filter, which results in an array of new objects being returned. This is because I want to filter the objects AFTER the transformation is applied and display the results of the transformation. However, I end up with an infinite digest because the objects I display are different than the objects I put in (when comparing their $$ids
). My thoughts to solve this are the following:
Use a tracking expression like
track by item.id
and assign the original objects'ids
to each of the transformed objects. While all my objects currently do have anid
, this seems like a bad idea because it makes the filter much less general- the original objects must have anid
, the transformation must not set anid
(as it will be overwritten), etc.Assign the original object's
$$id
to the transformed objects. This seems hackish, based on my understanding$$id
is supposed to be read only.Return a subset of the original objects based on the result of the transformation's filtering. This may cause performance issues as the transformation needs to be applied in both the filter and the display expression, AND I have to loop back through the transformed / filtered items to select the right original ones to return.
Here is the filter:
listModule.filter('ui.filter.transformFilter',
['$filter',
'$id',
function($filter, $id)
{
var Filter = $filter('filter');
return function(objects, transformer, expression) {
// precondition- we need a list of objects
if (!_.isArray(objects)) {
return objects;
}
var transformed = [];
for (var i = 0; i < objects.length; i++) {
transformed[i] = transformer(objects[i]);
}
return filtered = Filter(transformed, expression);
}
}]
);
And here is how I am trying to use it:
<tr ng-repeat="item in list.items | ui.filter.transformFilter:list.transformerFunction:list.search" ng-click="list.select({'item': item})" class="list-item">
<td ng-repeat="label in list.labels" ng-bind-html="item[label.key]"></td>
</tr>
Oh, and ideally ngClick
returns the original object, but I can always wrap a function around it to look that up.
One solution to this problem where you have an idempotent function that Angular, due to object IDs, thinks is not idempotent (and thus causes the $digest loop issue as you noted) is to use lo-dash/underscore's _.memoize
to cache your function's results.
This will guarantee that for any given cache key your filter will always return a completely identical object (including $$id
). This way you don't have to play games with $$id
and you get the performance benefit of not having to recompute the filter results on each $digest loop.
Here's how you could cache your filter's results:
return _.memoize(function(objects, transformer, expression) { ... },
function(objects,transformer,expression){
return objects +transformer.name + expression;
});
One important note for your situation is that by default _.memoize
uses the first function parameter (objects
in this case) as the cache key. Since your filter likely produces different results given different transformer functions and expressions I've added the optional second parameter- a hash function that uses objects
,expression
, and the name of the transformer
function to produce a cache key.
Here's a simplified version of your code using this: fiddle
这篇关于AngularJs - ngRepeat 带有返回新对象的过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!