21
votes

I've got the following json data returned from a service request:

{
    "entries": [{
        "id": 2081,
        "name": "BM",
        "niceName": "bodmas"
        }]
    }, {
        "id": 8029,
        "name": "Mas",
        "niceName": "Masm"
        }]
    }],
    "count": 2
}

And I am trying the following code in html to loop through this data:

<option ng-repeat="entry in entries" value="{{entry.name}}">{{entry.name}}</option>

I get the following error when I run the code:

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: entry in entries, Duplicate key: string:c

Following is the code for my controller:

myApp.controller("MyController", ['$scope', '$http', '$log', function($scope, $http, $log){
       ...

       $http.get('https://myServiceURL').success(function(data){
                    $scope.entries = data;
        });
}]);

Could somebody help me understand why am I getting that error?

6
Shouldn't you do $scope.entries = data.entries; in your controller ?Goodzilla
Your error message doesn't seem to belong to that snippet of code you provided. It mentions make in makes when your example says entry in entries. Do you have a list of strings called makes in scope? If so, that is the thing that needs the track by added.Martin Atkins
@MartinAtkins: I tried to simplify things using entry for make and entries for makes, mistakenly forgot to modify the error message. Sorry.skip
Did you try only adding track by $index and not changing your other expressions to $index? e.g <option ng-repeat="entry in entries track by $index" value="{{entry.name}}">{{$entry.name}}</option>Martin Atkins

6 Answers

38
votes

Add track by $index to your ng repeat so instead of:

<option ng-repeat="entry in entries" value="{{entry.name}}">{{entry.name}}</option>

Try:

<option ng-repeat="entry in entries track by $index" value="{{entry.name}}">{{entry.name}}</option>

There's further information about this in the documentation for this error message:

Occurs if there are duplicate keys in an ngRepeat expression. Duplicate keys are banned because AngularJS uses keys to associate DOM nodes with items.

By default, collections are keyed by reference which is desirable for most common models but can be problematic for primitive types that are interned (share references).

11
votes

Your JSON is invalid and should be :

{
    "entries": [{
        "id": 2081,
        "name": "BM",
        "niceName": "bodmas"
    }, {
        "id": 8029,
        "name": "Mas",
        "niceName": "Masm"
    }],
    "count": 2
}

Also, make sure you are accessing the document at the right level, use :

$http.get('https://myServiceURL').success(function(data){
    $scope.entries = data.entries;
});

Then, it should work. See this JSBin.

7
votes

If I may add an additional reason as to why this can occur...

If you are doing this with a JS object [] or {}

and you are passing it in to a directive like this

<my-directive my-attribute="{{ myObject }}"></my-directive>

Inside the directive you must turn myObject back into an object by doing this

...
controller: function( $scope ){

  $scope.links = $scope.$eval( $scope.myObject );
....

Then the HTML and ng-repeat will work

...
<span class="" ng-repeat="link in links">
...

ngRepeat does not know how to repeat over a single string.

Here is what the object would look like before $scope.$eval

"[{ hello: 'you' }]"

and after $scope.$eval()

[{ hello: 'you' }] an actual object to repeat over.

The error kind of makes a reference that it cannot repeat of a string. Here is the error that I got.

Error: [ngRepeat:dupes] http://errors.angularjs.org/1.3.0-beta.8/ngRepeat/dupes?p0=link%20in%20links&p1=string%3Al

1
votes

It looks like you have a problem with the structure of the data in your scope. Your example JSON shows an object with an entries property and a count property. You then put that whole object in your scope as entries. This means you'd need to access the entries as entries.entries, with the count in entries.count. Perhaps this controller is closer to what you wanted:

myApp.controller("MyController", ['$scope', '$http', '$log', function($scope, $http, $log){
    ...

    $http.get('https://myServiceURL').success(function(data){
        $scope.entries = data.entries;
        $scope.count = data.count;
    });
}]);
-1
votes

// To allow this Web Service to be called from script, using ASP.NET AJAX,uncomment the following line.

[System.Web.Script.Services.ScriptService] ==> Uncomment this line If you use .NET Service

-1
votes

duplicates in In ng-repeat is not allowed . Example

    <!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="angular.js"></script>

</head>
<body>
    <div ng-app="myApp" ng-controller="personController">
        <table>
            <tr> <th>First Name</th> <th>Last Name</th> </tr>
            <tr ng-repeat="person in people track by $index">
                <td>{{person.firstName}}</td>
                <td>{{person.lastName}}</td>
                <td><input type="button" value="Select" ng-click="showDetails($index)" /></td>
            </tr>

        </table> <hr />
        <table>
            <tr ng-repeat="person1 in items track by $index">
                <td>{{person1.firstName}}</td>
                <td>{{person1.lastName}}</td>
            </tr>
        </table>
        <span>   {{sayHello()}}</span>
    </div>
    <script> var myApp = angular.module("myApp", []);
        myApp.controller("personController", ['$scope', function ($scope)
        { 
            $scope.people = [{ firstName: "F1", lastName: "L1" },
                { firstName: "F2", lastName: "L2" }, 
                { firstName: "F3", lastName: "L3" }, 
                { firstName: "F4", lastName: "L4" }, 
                { firstName: "F5", lastName: "L5" }]
            $scope.items = [];
            $scope.selectedPerson = $scope.people[0];
            $scope.showDetails = function (ind) 
            { 
                $scope.selectedPerson = $scope.people[ind];
                $scope.items.push($scope.selectedPerson);
            }
            $scope.sayHello = function ()
            {
                return $scope.items.firstName;
            }
        }]) </script>
</body>
</html>