AngularJs - ng使用返回新对象的过滤器重复

时间:2013-11-27 17:35:15

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

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

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

  2. 将原始对象的id分配给已转换的对象。根据我的理解,这看起来很简陋$$id应该是只读的。

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

  4. 以下是过滤器:

    $$id

    以下是我尝试使用它的方法:

    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> 会返回原始对象,但我总是可以用它包围一个函数来查看它。

2 个答案:

答案 0 :(得分:7)

这个问题的一个解决方案是你有一个幂等函数,因为对象ID认为Angular不是幂等的(因此导致你记下的$ digest循环问题)是使用lo-dash / underscore的_.memoize缓存您的功能结果

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

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

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

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

以下是使用此代码的简化版代码:fiddle

答案 1 :(得分: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>

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

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