AngularJs - ngRepeat 带有返回新对象的过滤器 [英] AngularJs - ngRepeat with a filter that returns a new object

查看:22
本文介绍了AngularJs - ngRepeat 带有返回新对象的过滤器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对过滤器中的对象应用转换,这会导致返回一组新对象.这是因为我想在应用转换后过滤对象并显示转换结果.但是,我最终得到了一个无限摘要,因为我显示的对象与我放入的对象不同(比较它们的 $$ids 时).我解决这个问题的想法如下:

  1. 使用类似track by item.id 的跟踪表达式,并将原始对象的ids 分配给每个转换后的对象.虽然我所有的对象目前都有一个 id,但这似乎是一个坏主意,因为它使过滤器变得不那么通用——原始对象必须有一个 id,转换必须不设置 id(因为它会被覆盖)等

  2. 将原始对象的 $$id 分配给转换后的对象.根据我的理解,$$id 应该是只读的.

  3. 根据转换的过滤结果返回原始对象的子集.这可能会导致性能问题,因为需要在过滤器和显示表达式中应用转换,而且我必须循环返回已转换/已过滤的项目以选择要返回的正确原始项目.

这里是过滤器:

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)作为缓存键.由于给定不同的转换器函数和表达式,您的过滤器可能会产生不同的结果,因此我添加了可选的第二个参数 - 一个使用 objectsexpression 和名称的哈希函数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:

  1. 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 an id, this seems like a bad idea because it makes the filter much less general- the original objects must have an id, the transformation must not set an id (as it will be overwritten), etc.

  2. Assign the original object's $$id to the transformed objects. This seems hackish, based on my understanding $$id is supposed to be read only.

  3. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆