ng-repeat中的AngularJS“标题”

时间:2013-06-28 19:27:58

标签: javascript design-patterns angularjs angularjs-ng-repeat ng-repeat

我遇到的问题比最近出现的情况更多,而且我想知道最好的方法。简单说明一下:

我将数据显示在ng-repeat中,按特定项目排序。比如说它按名称排序。我的目标是在字母顺序列表中的字母处打开标题:

----A----
Abe Lincoln
Adam Smith
----B----
Barack Obama
Barry Zuckercorn
----C----
...

等等。

我尝试过的事情包括:

  • 让控制器完全重建模型的数据,手动将其放入一组字母组中。例如,我的服务有一系列“帖子”,我的控制器,当服务更新时,手动将那些“帖子”洗牌成“letterGroups”数组。然后可能只有两个嵌套的ng-repeats

然而,这依赖于对数据的大量操作,并且不容易动态更改数据的排序。

  • 当模型更新时,让控制器通过手动单步执行数据来“填充”数据,检查字母何时更改,并在该元素上搭载“标题”属性(post.header = true)。然后ng-repeat可以检查它当前所在的元素是否是标题,并使用ng-if将其他内容插入到DOM中。

这感觉稍微干净但是由于ng-repeat的工作方式,标题元素必须“包含”在与ng重复元素相同的水平。例如,如果对<tr>元素执行ng-repeat,则意味着无法为标题插入另一个<tr>,因为特殊元素必须在内部 em> <tr>

最后,按照与上述相同的方式:

  • 让控制器维护一个“关键索引”列表 - 这样做的好处是不像上面的第二种方法那样修改数据,但通常以相同的方式工作。

编辑:

我撰写了一篇博文here,解释了我最终如何处理这个问题 - 感谢由Ian的答案链接的Google网上论坛讨论。

2 个答案:

答案 0 :(得分:5)

创建角度过滤器。它可以接受排序列表,按第一个字母对其进行分块,并为每个块返回一个对象,其中包含字母和以该字母开头的对象数组。

例如参见chunk by size filter here。请特别注意有关为分块值创建哈希值的讨论。

答案 1 :(得分:1)

我写了一个小指令,它监视对象并自动将$ header属性添加到列表中的第一个属性。像这样使用它:

 <div ng-repeat="item in data.items | orderBy:... | filter:... as filteredItems" header-list objects="filteredItems" header="getHeader">
    <div ng-show="item.$header">{{item.$header}}</div>
    <div>Your normal content</div>
 </div>

您可以根据需要传递构建标题的函数。该函数可以是范围中的实例方法或函数。要获得第一个字母,请在控制器中定义。

$scope.getHeader = function(item) {
  return item.name.slice(0, 1);
}, 

当过滤器和订单发生变化时,列表会自动更新。

app.module("...").directive('headerList', function () {
  return {
    restrict: 'A',
    scope: {
      objects: '=',
      header: '=' // This must be a function or instance method
    },
    link: function(scope) {
      scope.$watch('objects', function() {

        var lastHeader, currentHeader;

        scope.objects.forEach(function (obj) {
          // We pass obj twice, so it can be used is instance method or regular function that takes the object as argument.
          currentHeader = scope.header.call(obj, obj);

          // in order to display a header per type, we mark the first event of each kind as header
          if (currentHeader !== lastHeader) {
            obj.$header = currentHeader;
            lastHeader = currentHeader;
          } else {
            obj.$header = null;
          }
        });

      });
    }
  }
});