【问题标题】:AngularJs - ngRepeat with a filter that returns a new objectAngularJs - 带有返回新对象的过滤器的 ngRepeat
【发布时间】:2013-12-13 11:55:31
【问题描述】:

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

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

  2. 将原始对象的$$id 分配给转换后的对象。这似乎很骇人听闻,根据我的理解,$$id 应该是只读的。

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

这是过滤器:

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);
    }                  
  }]
);

这是我尝试使用它的方式:

  <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>

哦,理想情况下 ngClick 返回原始对象,但我总是可以在它周围包装一个函数来查找它。

【问题讨论】:

  • 我想我知道解决方案 :) 我正在努力。
  • 你有list.labels的一些样本数据吗?

标签: angularjs angularjs-ng-repeat angularjs-filter


【解决方案1】:

如果你有一个幂等函数,Angular 的一个解决方案,由于对象 ID,它认为它不是幂等的(因此导致 $digest 循环问题,正如你所指出的)是 使用 lo-dash/underscore's _.memoize 缓存函数的结果

这将保证对于任何给定的缓存键,您的过滤器将始终返回完全相同的对象(包括$$id)。这样您就不必使用$$id 玩游戏,并且您可以获得无需在每个 $digest 循环上重新计算过滤结果的性能优势

以下是缓存过滤器结果的方法:

return _.memoize(function(objects, transformer, expression) { ... },
                 function(objects,transformer,expression){ 
                    return objects +transformer.name + expression;
                  });   

对于您的情况,一个重要的注意事项是默认情况下_.memoize 使用第一个函数参数(在本例中为objects)作为缓存键。由于给定不同的转换器函数和表达式,您的过滤器可能会产生不同的结果,因此我添加了可选的第二个参数 - 一个使用 objectsexpression 的散列函数和 transformer 函数的名称来生成缓存键。

这是您的代码的简化版本:fiddle

【讨论】:

    【解决方案2】:

    Angular 使用angular.equals 来检测范围变化。跟踪表达式用于将数组项与 DOM 元素进行匹配,这样当您过滤或重新排序数组时,Angular 只会显示、隐藏或重新排序某些元素,而不是重建整个子树。

    看看这个简单的例子:http://jsfiddle.net/Nb8mX/

    function Ctrl($scope) {
    
        $scope.transform = function(item) {
            item.abc *= 2;
            return true;
        };
    
        $scope.data = [
            {abc: 123, def: 1},
            {abc: 456, def: 2},
            {abc: 789, def: 3}
        ];
    
    }
    

    无论track by如何,通过过滤器修改项目都会导致无限摘要循环,而在ng-init内部执行相同操作则不会出现问题:

    <ul ng-app="blah" ng-controller="Ctrl">
        <li ng-repeat="item in data | filter : transform">{{ item.abc }}</li>
        <li ng-repeat="item in data | filter : transform track by item.def">{{ item.abc }}</li>
        <li ng-repeat="item in data" ng-init="transform(item)">{{ item.abc }}</li>
    </ul>
    

    这表明您可以编写指令而不是过滤器。

    如果有人知道更好的解决方案,我也很乐意学习。

    【讨论】:

      猜你喜欢
      • 2013-06-11
      • 2016-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多