$index of Object in Array while using ng-repeat and a filter

All we need is an easy explanation of the problem, so here it is.

Im fairly new to angular and have been able to get around somewhat. But I cant seem to find the answer to this scenario…

I have an array of objects, which I am pulling down from firebase. I am using an ng-repeat for the objects, then displaying data accordingly. I am trying to pass the index as a routeparam to an “edit” controller. In which case I would like to pull the object data as one would anticipate. However, when I filter the ng-repeat I am getting the index of the filtered content. where am I going wrong in finding the true index?

  .config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
  .when('/profiles/:index', {
    templateUrl: '../views/profile.html',
    controller: 'profileCtrl'
  });

Route is above, Controller is below

  .controller('profileCtrl', function( $scope, $routeParams ){

$scope.teamProfile = $scope.ourTeam[$routeParams.index];
    $scope.index = $routeParams.index;
});

And finally the snippet of html from within the repeat.

<div class="profileName"><a href="/profiles/{{$index}}" rel="nofollow noreferrer noopener">{{member.name}}</a><span class="handle">{{member.handle}}</span></div>

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Try this :

<div ng-repeat="member in members">
{{members.indexOf(member)}}
</div>

indexOf always returns the original index in an ng-repeat

Demo

Method 2

Unfortunately $index is only the “iterator offset of the repeated element (0..length-1)”

If you want the original index you would have to add that to your collection before filtering, or simply not filter the elements at all.

One possible approach:

angular.forEach(members, function(member, index){
   //Just add the index to your item
   member.index = index;
});

<div ng-repeat="member in members">
   <a href="/profiles/{{member.index}}" rel="nofollow noreferrer noopener">
</div>

Now, it also seems that this kind of information is really more like an ID than anything else. Hopefully that is already part of the record, and you can bind to that instead of using the index.

Method 3

You could use a function to return the index from the array

<div ng-repeat="post in posts | orderBy: '-upvotes'">
   <a href="#/posts/{{getPostIndex(post)}}" rel="nofollow noreferrer noopener"></a> 
</div>

And the function

$scope.getPostIndex = function (post) {
    return $scope.posts.indexOf(post); //this will return the index from the array
}

On my example I have an array of objects called “posts”, on which I use a filter to order them by one of their properties (“upvotes” property). Then, in the “href” attribute I call “getPostIndex” function by passing it by reference, the current object.

The getPostIndex() function simply returns the index from the array by using Javascript array indexOf() method.

The nice thing about this is that this solution is not tied to a specific filter (like in @holographix answer) and will work for all of them.

Method 4

I just stumbled across the same problem and I found this supertrick on the angular git issues

items.length - $index - 1

like

    <div ng-repeat="item in ['item', 'item', 'item'] | reversed">
      <!-- Original index: 2, New index: 0 -->
      <p>Original index: {{items.length - $index - 1}}, New index: {{$index}}</p>
      <!-- Original index: 1, New index: 1 -->
      <p>Original index: {{items.length - $index - 1}}, New index: {{$index}}</p>
      <!-- Original index: 0, New index: 2 -->
      <p>Original index: {{items.length - $index - 1}}, New index: {{$index}}</p>
    </div>

if you’re in trouble like me give it a shot:

https://github.com/angular/angular.js/issues/4268

Method 5

You can inject $route and use $route.current.params.index to get the value.

.controller('profileCtrl', function( $scope, $route ) {

     $scope.index = $route.current.params.index;

});

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply