8

I have items I want to show, normally by using ng-repeat. I want to show the in some order (easy), but whenever the ordered attribute changes, I want some HTML in-between.

Example: (Fiddle):

<div ng-app ng-controller="Main">
   <div ng-repeat="item in items | orderBy:'role'">
        {{item.role}} - {{item.name}}
   </div>
</div>

function Main($scope){
    $scope.items = [{name: 'First', role: 1}, 
                    {name: 'Second', role:2}, 
                    {name: 'Third', role: 1}, 
                    {name: 'Fourth', role: 2}];
    }

I want it to print:

1 - First
1 - Third
(some separator kode)
2 - Second
2 - Fourth

Matsemann
  • 21,083
  • 19
  • 56
  • 89
  • See if this helps: http://stackoverflow.com/a/14076254/215945 – Mark Rajcok Apr 08 '13 at 21:32
  • @MarkRajcok thank you, that works. But I'd rather like not to make a copy as there are many attributes that it can be ordered on and a huge dataset. – Matsemann Apr 08 '13 at 21:48
  • If you have a huge dataset, you'll not want to pipe to a filter in your HTML, as I believe [this will be evaluated every digest cycle](http://stackoverflow.com/a/15646675/215945)! – Mark Rajcok Apr 09 '13 at 02:01

2 Answers2

15

You will want to create a function in your scope.

$scope.currentRole = 'something';
$scope.CreateHeader = function(role) {
      showHeader = (role!=$scope.currentRole); 
       $scope.currentRole = role;
      return showHeader;
}

And then in your HTML:

<div ng-app ng-controller="Main">
   <div ng-repeat="item in items | orderBy:'role'">
       <div ng-show="CreateHeader(item.role)">
       something here for the header
      </div>
        {{item.role}} - {{item.name}}

   </div>
</div>
lucuma
  • 18,247
  • 4
  • 66
  • 91
  • Thanks, this does the trick. I can skip the first one by doing ´ng-show="CreateHeader(item.role) && !$first"´ – Matsemann Apr 09 '13 at 07:11
  • I added ..(item.role, $last). Then when last==true I reset $scope.currentRole. This fixes an issue that occurs when the role doesn't change (ie since repeat runs multiple times, the last one is the same as first which hides the header). – brian h Aug 14 '13 at 19:47
  • 4
    This works perfectly, but how would be if I want to wrap each group within an html element?. My first attempt is to make double CreateHeader validation to open and close the html element but I think it is not the best way to achieve this. Is there any elegant workaround for this? – asumaran Aug 17 '13 at 01:18
  • This did not work for me. Angular refreshed my list (for some reason) and my function returned `false` because `currentRole` was set from the previous `ng-repeat` loop. – Brent Washburne Jan 22 '14 at 15:50
1

The solution by @lucuma only works on the first time through the loop. If Angular refreshes the list, the variable will still be set from the previous loop.

Instead, I added a new attribute to the list (say, header) during initialization:

function Main($scope){
    $scope.items = [{name: 'First', role: 1, header: false}, 
                    {name: 'Second', role:2, header: false}, 
                    {name: 'Third', role: 1, header: true}, 
                    {name: 'Fourth', role: 2, header: false}];
}

Then the HTML by @lucuma works:

<div ng-app ng-controller="Main">
  <div ng-repeat="item in items | orderBy:'role'">
    <div ng-show="item.header">    // <== with this change
      something here for the header
    </div>
    {{item.role}} - {{item.name}}
  </div>
</div>

Oh, and you could sort the list once at initialization and remove the orderBy filter.

Brent Washburne
  • 12,904
  • 4
  • 60
  • 82