Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys


#1

Hi, I’m trying to implement a list view which fetches emails from the server, caches them, and then fetches emails during a pull to refresh. In my template I show the email using ng-repeat:

<ion-refresher pulling-text="Pull to refresh" on-refresh="doRefresh()">
    	</ion-refresher>
		<ion-list>
        	<ion-item ng-repeat="email in emails" ui-sref="app.actions({emailId: email.id})">
          		<p>{{email.from}}</p>
          		{{email.subject}}
        	</ion-item>
       	</ion-list>

In my controller I fetch the Emails from an email service in the beginning and then fetch new emails on the doRefresh() function:

.controller('InboxCtrl', function($scope, $http, Emails) {
  $scope.emails = Emails.all();
  $scope.remove = function(email) {
    Emails.remove(email);
  }
  $scope.doRefresh = function() {
    Emails.refresh().then(function(resp) {
      console.log(resp);
      // For JSON responses, resp.data contains the result
      $scope.doneEmails = 6;
      $scope.emails = resp;
    }, function(err) {
      console.error('ERR', err);
      // err.status will contain the status code
    })
    .finally( function() {
      $scope.$broadcast('scroll.refreshComplete');
      console.log($scope.resource);
    });
  }
  $scope.doneEmails = 7;
  $scope.totalEmails = 18;
})

and then in the email service I actually fetch the emails from my server:

.factory('Emails', function($http) {
  // Might use a resource here that returns a JSON array
  
  // Some fake testing data
  // Some fake testing data
  var emails = [{
    id: 0,
    from: 'Ben Sparrow',
    to: 'Debnath Sinha',
    time: '21/01/2014',
    subject: 'First Email'
  }, {
    id: 1,
    from: 'Jane Austen',
    to: 'Debnath Sinha',
    time: '21/01/2014',
    subject: 'New novel'
  }, {
    id: 2,
    from: 'John Doe',
    to: 'Debnath Sinha',
    time: '21/01/2014',
    subject: 'Unknown Email'
  }];

  return {
    all: function() {
      return emails;
    },
    get: function(emailId) {
      // Simple index lookup
      return emails[emailId];
    },
    refresh: function() {
      return $http.get('http://localhost:8080/emailAPI').then(function(response){
        emails = response['data'];
        return emails;
      });
    }
  }
});

I expected that on pull-to-refresh, since I am writing to $scope, I should see the new emails returned from the server (will work later on the caching and appending pieces), but instead I get the error:

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys.

From what I can make of the error, its fetching and trying to replace, but running into duplicates? I tried making the ng-repeat line “email in emails track by email.id”, but no changes. The response from the server is:

 [{
  id: 3,
  from: 'Ben Sparrow',
  to: 'Debnath Sinha',
  time: '21/01/2014',
  subject: 'Email 3'
}, {
  id: 4,
  from: 'Ben Sparrow',
  to: 'Debnath Sinha',
  time: '21/01/2014',
  subject: 'Email 4'
}, {
  id: 5,
  from: 'Ben Sparrow',
  to: 'Debnath Sinha',
  time: '21/01/2014',
  subject: 'Email 5'
}, {
  id: 6,
  from: 'Ben Sparrow',
  to: 'Debnath Sinha',
  time: '21/01/2014',
  subject: 'Email 6'
}]

Do I need to do something explicit to render the new email list into the view?


#2

replace ng-repeat=“email in emails” with ng-repeat=“email in emails track by $index”.


#3

I already tried that, it just gave me a blank list that was very deep. I read up a bit on “track by $index”, and in my list, I actually do not have duplicates (I’m overwriting on the old emails), so I don’t see why that would cause an issue? I also tried “track by email.id” since they all have unique id’s, but that also produced the same error. Am I missing something?


#4

Any chance you could throw this into a codepen?


#5

Sorry it took so long, was trying to pare down the code into the minimal amount required. Here is the codepen:

In the “finally” section, $scope.emails shows as the one returned from the server (Email 3, Email 4, Email 5, Email 6), but its not what is showing up in the view.

Thanks!


#6

@mhartington in the above codepen, it seems to be fetching from the server just fine, but not applying it to the view. I’m wondering if this has something to do with scope inheritance though it didn’t seem like it.


#7

Hi, Did you have a solution ? I get the same mistake.


#8

just check your email array you may push the same data repeatedly. make sure that you are emptying the email array before adding the data to array