AngularJS Group by Directive without External Dependencies

Dar*_*ryl 27 group-by angularjs angularjs-directive angularjs-ng-repeat ng-show

我是Angular的新手,想学习处理问题的最佳方法.我的目标是有一个可重用的方法来创建按标题分组.我创建了一个可行的解决方案,但我认为这应该是一个指令,而不是我的控制器中的范围函数,但我不知道如何实现这一点,或者指令是否是正确的方法.任何投入将不胜感激.

看看我目前处理jsFiddle的方法

在HTML中,它是一个使用ng-repeat的简单列表,我在ng-show上调用newGrouping()函数.该函数传递对完整列表的引用,我想要分组的字段和当前索引.

<div ng-app>
<div ng-controller='TestGroupingCtlr'>
    <div ng-repeat='item in MyList'>
        <div ng-show="newGrouping($parent.MyList, 'GroupByFieldName', $index);">
            <h2>{{item.GroupByFieldName}}</h2>
        </div>
        {{item.whatever}}
    </div>
</div>
</div>
Run Code Online (Sandbox Code Playgroud)

在我的控制器中,我有newGrouping()函数,它只是将当前值与之前的值进行比较,第一项除外,并根据匹配返回true或false.

function TestGroupingCtlr($scope) {

  $scope.MyList = [
    {GroupByFieldName:'Group 1', whatever:'abc'},
    {GroupByFieldName:'Group 1', whatever:'def'},
    {GroupByFieldName:'Group 2', whatever:'ghi'},
    {GroupByFieldName:'Group 2', whatever:'jkl'},
    {GroupByFieldName:'Group 2', whatever:'mno'}
  ];

  $scope.newGrouping = function(group_list, group_by, index) {
  if (index > 0) {
    prev = index - 1;
    if (group_list[prev][group_by] !== group_list[index][group_by]) {
      return true;
    } else {
      return false;
    }
  } else {
    return true;
  }
  };
}
Run Code Online (Sandbox Code Playgroud)

输出将如下所示.

第1组

  • ABC
  • 高清

第2组

  • GHI
  • JKL
  • MNO

感觉应该有更好的方式.我希望这是一个可以重用的常用实用函数.这应该是指令吗?是否有更好的方法来引用列表中的上一项而不是传递完整列表和当前索引的方法?我将如何处理此指令?

任何意见是极大的赞赏.

更新:寻找不需要外部依赖的答案.使用下划线/ lodash或角度滤波器模块有很好的解决方案.

达里尔

Jos*_*hMB 34

这是对上述Darryl解决方案的修改,允许多个参数组.此外,它还使用$ parse来允许使用嵌套属性作为分组参数.

使用多个嵌套参数的示例

http://jsfiddle.net/4Dpzj/6/

HTML

<h1>Multiple Grouping Parameters</h1>
<div ng-repeat="item in MyList  | orderBy:'groupfield' | groupBy:['groupfield', 'deep.category']">
    <h2 ng-show="item.group_by_CHANGED">{{item.groupfield}} {{item.deep.category}}</h2>
     <ul>
        <li>{{item.whatever}}</li>
     </ul>
</div>  
Run Code Online (Sandbox Code Playgroud)

过滤(Javascript)

app.filter('groupBy', ['$parse', function ($parse) {
    return function (list, group_by) {

        var filtered = [];
        var prev_item = null;
        var group_changed = false;
        // this is a new field which is added to each item where we append "_CHANGED"
        // to indicate a field change in the list
        //was var new_field = group_by + '_CHANGED'; - JB 12/17/2013
        var new_field = 'group_by_CHANGED';

        // loop through each item in the list
        angular.forEach(list, function (item) {

            group_changed = false;

            // if not the first item
            if (prev_item !== null) {

                // check if any of the group by field changed

                //force group_by into Array
                group_by = angular.isArray(group_by) ? group_by : [group_by];

                //check each group by parameter
                for (var i = 0, len = group_by.length; i < len; i++) {
                    if ($parse(group_by[i])(prev_item) !== $parse(group_by[i])(item)) {
                        group_changed = true;
                    }
                }


            }// otherwise we have the first item in the list which is new
            else {
                group_changed = true;
            }

            // if the group changed, then add a new field to the item
            // to indicate this
            if (group_changed) {
                item[new_field] = true;
            } else {
                item[new_field] = false;
            }

            filtered.push(item);
            prev_item = item;

        });

        return filtered;
    };
}]);
Run Code Online (Sandbox Code Playgroud)


man*_*nta 23

如果您已经在使用LoDash/Underscore或任何功能库,则可以使用_.groupBy()(或类似名称)函数执行此操作.


在控制器中:

var movies = [{"movieId":"1","movieName":"Edge of Tomorrow","lang":"English"},
              {"movieId":"2","movieName":"X-MEN","lang":"English"},
              {"movieId":"3","movieName":"Gabbar Singh 2","lang":"Telugu"},
              {"movieId":"4","movieName":"Resu Gurram","lang":"Telugu"}];
$scope.movies = _.groupBy(movies, 'lang');
Run Code Online (Sandbox Code Playgroud)

在模板中:

<ul ng-repeat="(lang, langMovs) in movies">{{lang}}
  <li ng-repeat="mov in langMovs">{{mov.movieName}}</li>
</ul>
Run Code Online (Sandbox Code Playgroud)

这将呈现:

英语

  • 明天之边缘
  • X战警

泰卢固语

  • Gabbar Singh 2
  • Resu Gurram

更好的是,这也可以非常容易地转换为过滤器,没有太多的样板代码来按属性对元素进行分组.

更新:按多个键分组

通常使用多个键进行分组非常有用.例如,使用LoDash(来源):

$scope.movies = _.groupBy(movies, function(m) {
    return m.lang+ "-" + m.movieName;
});
Run Code Online (Sandbox Code Playgroud)

更新我推荐此方法的原因:ng-repeat/ 上使用过滤器ng-options导致严重的性能问题,除非过滤器快速执行.谷歌针对过滤器性能问题.你会知道的!