Skip to content
This repository was archived by the owner on Oct 2, 2019. It is now read-only.
This repository was archived by the owner on Oct 2, 2019. It is now read-only.

[ngRepeat:dupes] Duplicates in repeater are not allowed. Broken after angular/ui/select upgrade #366

@davisford

Description

@davisford

I have what should amount to a fairly simple use of ui-select, in fact I've been running it in production for months now with a pinned version of angular-ui and angular b/c things have been a bit dicey trying to get angular-ui working with angular 1.3.2. Well, today, I'm trying to update the dependencies and work through some of these issues, and this ui-select broke in the process. Here's output from bower on the versions:

├── angular#1.3.2-build.3514+sha.d906ed3
   /* removed other angular modules, e.g. angular-route, etc. */
├─┬ angular-ui-bootstrap-bower#0.11.2
├─┬ angular-ui-select#0.8.3

This is the error being thrown after the upgrade:

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: $item in $select.selected, Duplicate key: string:u, Duplicate value: "u"
http://errors.angularjs.org/1.3.2-build.3514+sha.d906ed3/ngRepeat/dupes?p0=%24item%20in%20%24select.selected&p1=string%3Au&p2=%22u%22
    at http://localhost:3000/angular/angular.js:80:12
    at ngRepeatAction (http://localhost:3000/angular/angular.js:24073:21)
    at Object.$watchCollectionAction [as fn] (http://localhost:3000/angular/angular.js:13819:13)
    at Scope.$digest (http://localhost:3000/angular/angular.js:13952:29)
    at Scope.$apply (http://localhost:3000/angular/angular.js:14214:24)
    at done (http://localhost:3000/angular/angular.js:9474:47)
    at completeRequest (http://localhost:3000/angular/angular.js:9659:7)
    at XMLHttpRequest.requestLoaded (http://localhost:3000/angular/angular.js:9602:9) angular.js:11339(anonymous function) angular.js:11339(anonymous function) angular.js:8415Scope.$digest angular.js:13970Scope.$apply angular.js:14214done angular.js:9474completeRequest angular.js:9659requestLoaded

Obviously, this is a well-known error, and nicely documented, to boot...the problem is track by doesn't seem to resolve the issue, and besides, I'm binding to an array of strings in the repeat, which shouldn't need track by AFAIK, because it ought to just use the array index (I thought).

Here's what the code looks like:

<ui-select multiple ng-model='user.roles' theme='bootstrap'>
  <ui-select-match placeholder='Select a role...'> {{$item}} </ui-select-match>
  <ui-select-choices repeat='role in roles track by $index'> {{role}} </ui-select-choices>
</ui-select>

I added the track by $index but it seems to make no difference.

The controller:

// Roles and Accounts are ngResource
angular.module('app').controller('MyCtrl', ['$scope', 'Roles', 'Accounts', function ($scope, Roles, Accounts) {

  // ui-select requires these to be bindable even before ngResource is resolved
  $scope.user = { roles: [ ] };
  $scope.roles = [ ];

  Roles.query().$promise.then(function (res) {
    $scope.roles = res.roles;
    // i.e. [ 'user', 'admin', 'superuser' ]
  }, handleError);

  Accounts.get({id: id}).$promise.then(function(user) {
    $scope.user = user;
  }, handleError);

}]);

The interesting thing is if I breakpoint the error in Angular, it seems to be doing ng-repeat on each character of each string in the array, as opposed to each individual word in the array. This is why it throws the error, because the word 'superuser' contains two u characters - which it considers a duplicate.

screen shot 2014-11-03 at 1 15 57 pm

I haven't the foggiest why it is behaving this way...any ideas are more than welcome.

This is the DOM HTML for this element, copied out of Chrome DevTools (after the error is thrown):

<div class="ui-select-multiple ui-select-bootstrap dropdown form-control ng-valid" ng-class="{open: $select.open}" multiple="multiple" ng-model="user.roles" theme="bootstrap">
  <div>
    <span class="ui-select-match" placeholder="Select a role...">
      <!-- ngRepeat: $item in $select.selected -->
    </span>
    <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="ui-select-search input-xs ng-pristine ng-valid ng-touched" placeholder="Select a role..." ng-disabled="$select.disabled" ng-hide="$select.disabled" ng-click="$select.activate()" ng-model="$select.search">
  </div>
  <ul class="ui-select-choices ui-select-choices-content dropdown-menu ng-scope" role="menu" aria-labelledby="dLabel" ng-show="$select.items.length > 0" repeat="role in roles track by $index">
    <li class="ui-select-choices-group">
      <div class="divider ng-hide" ng-show="$select.isGrouped &amp;&amp; $index > 0"></div>
      <div ng-show="$select.isGrouped" class="ui-select-choices-group-label dropdown-header ng-binding ng-hide"></div>
      <!-- ngRepeat: role in $select.items track by $index -->
      <div class="ui-select-choices-row ng-scope" ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}" ng-repeat="role in $select.items track by $index" ng-mouseenter="$select.setActiveItem(role)" ng-click="$select.select(role)"><a href="javascript:void(0)" class="ui-select-choices-row-inner" uis-transclude-append=""><span class="ng-binding ng-scope">user</span></a></div>
      <!-- end ngRepeat: role in $select.items track by $index -->
      <div class="ui-select-choices-row ng-scope" ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}" ng-repeat="role in $select.items track by $index" ng-mouseenter="$select.setActiveItem(role)" ng-click="$select.select(role)"><a href="javascript:void(0)" class="ui-select-choices-row-inner" uis-transclude-append=""><span class="ng-binding ng-scope">admin</span></a></div>
      <!-- end ngRepeat: role in $select.items track by $index -->
      <div class="ui-select-choices-row ng-scope" ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}" ng-repeat="role in $select.items track by $index" ng-mouseenter="$select.setActiveItem(role)" ng-click="$select.select(role)"><a href="javascript:void(0)" class="ui-select-choices-row-inner" uis-transclude-append=""><span class="ng-binding ng-scope">superuser</span></a></div>
      <!-- end ngRepeat: role in $select.items track by $index -->
    </li>
  </ul>
  <input ng-disabled="$select.disabled" class="ui-select-focusser ui-select-offscreen ng-scope" type="text" aria-haspopup="true" role="button">
</div>

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions