-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[ngRepeat:dupes] Duplicates in repeater are not allowed. Broken after angular/ui/select upgrade #366
Description
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.
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 && $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>