2

I have a weird problem that I'm not able to reproduce on Plunker. When the first option 'user' is set in the model, the select displays it properly and it is set to 'user'. When I load another user with role:'admin', the select is set to blank option.

In my controller I define:

$scope.roles = ['user', 'admin'];

My $scope.user object looks like this:

{
   "_id": "54f1f8f7e01249f0061c5088",
   "displayName": "Test1 User",
   "provider": "local",
   "username": "test1",
   "__v": 0,
   "updated": "2015-03-02T07:41:42.388Z",
   "created": "2015-02-28T17:20:55.893Z",
   "role": "admin",
   "profileImageURL": "modules/users/img/profile/default.png",
   "email": "test1@user.com",
   "lastName": "User",
   "firstName": "Test1"
}

In my view:

<select id="role" class="form-control" data-ng-model="user.role" data-ng-options="role for role in roles"></select>

When I reverse the roles array $scope.roles = ['admin', 'user']; then admins are displayed properly, and 'user' won't be set as selected.

Strangely if I add a third item to the array $scope.roles = ['user', 'admin', 'joe']; Then the first two 'user' and 'admin' will be set selected properly, and the last one 'joe' won't.

Any ideas?

--- UPDATE ---

The generated select markup looks like this:

<select id="role" class="form-control ng-pristine ng-valid" data-ng-options="role for role in roles" data-ng-model="user.role">
    <option value="? string:joe ?"></option>
    <option value="0" selected="selected" label="admin">admin</option>
    <option value="1" label="user">user</option>
    <option value="2" label="joe">joe</option>
</select>
orszaczky
  • 13,301
  • 8
  • 47
  • 54
  • Well, as you said it's [not reproducible](http://jsfiddle.net/iluzyanin/w1w28fg7/) with given info. Try looking at actual markup behind `select` - what do you see there for last option? – Ilya Luzyanin Mar 02 '15 at 10:16
  • Please see markup in question update – orszaczky Mar 02 '15 at 11:17
  • Looks [familiar](http://stackoverflow.com/questions/16783294/angular-adds-strange-options-into-select-element-when-setting-model-value)? – Ilya Luzyanin Mar 02 '15 at 11:25
  • Yes, thanks for the link. Apparently this is happens when the options are in an array. – orszaczky Mar 02 '15 at 11:59
  • When you first load the page - what's the initial value of `user.role`? I mean at the moment when angular does the binding. – Ilya Luzyanin Mar 02 '15 at 12:03
  • How can I check that? I'm not setting any specific value at init. – orszaczky Mar 02 '15 at 12:12
  • Well, you basically declare your `$scope.user` object in controller and initialize it with some value (maybe empty object), right? So, try setting `$scope.user.role= $scope.roles[0]` in your controller, so it would be initialized with some default value (reference to existing `roles` index) before actual user data is obtained. – Ilya Luzyanin Mar 02 '15 at 12:16
  • Unfortunately this doesn't make a difference. – orszaczky Mar 02 '15 at 12:30

4 Answers4

1

Just got done battling this. My model $scope.details.source, is being populated by ajax, causing the issue. (i.e. select would not init to last item in array when that was returned by ajax, other values init'ed OK). I did find a work around. I had

$scope.sources = ["1", "2", "3", "4", "5", "6", "7", "8"];
<select ng-model="details.source" ng-options="item for item in sources"> </select>

and changed it to:

$scope.sources = [{index:0, value:"1"}, {index:1, value:"2"}, {index:2, value:"3"}, {index:3, value:"4"}, {index:4, value:"5"}, {index:5, value:"6"}, {index:6, value:"7"}, {index:7, value:"8"}];
<select ng-model="details.source" ng-options="item.index as item.value for item in sources"> </select>

I found that the problem didn't exist when the model was initialized in the controller as opposed to ajax. Donno why ... but it works.

liteflier
  • 327
  • 2
  • 8
0

I'm posting a workaround, but not a solution nor explanation for the problem above. If anyone has actual info why this is happening, and how to make this work with arrays, please share and I'll be happy to accept it as an answer.

I changed the options from a simple array to an array of objects:

$scope.roles = [{name:'Admin',value:'admin'},{name:'User', value:'user'}];

And my view to:

<select data-ng-model="user.role" data-ng-options="role.value as role.name for role in roles"></select>

Thanks to Ilya Luzyanin for the help and directions!

Community
  • 1
  • 1
orszaczky
  • 13,301
  • 8
  • 47
  • 54
0

I made all combinations with you suggest, but the error doesn't occur.

Please look for my tests:

http://plnkr.co/edit/jzfhD3D60fhj8yFqBqgJ?p=preview

<h3>select</h3>
<select ng-model="role" ng-options="item for item in roles"></select>
<h3>original</h3>
<select id="role" class="form-control" data-ng-model="user.role" data-ng-options="role for role in roles"></select>

Try to verify your angularjs version. Maybe can be a specific version error.

Joao Polo
  • 2,153
  • 1
  • 17
  • 26
  • You could actually reproduce the error in the top select. If you check the html source, you'll see the first blank option: `` My user object is loaded from an API call and apparently this happens when the model is not available at the time of binding. Strangely this doesn't happen when the options are in objects. – orszaczky Mar 04 '15 at 13:03
  • I extended your plunker including my object-based workaround, and trying to emulate the api call with setTimeout(). The interesting thing happens when you open the 'old' select, nothing happens, but as soon as you open the 'object' select, it updates the binding. This is not the exact same behavior I had, but it does show that there is some difference in Angular handling arrays and objects. See here: http://plnkr.co/edit/WcAfmgwJkmJVfLFAPi2Z?p=preview – orszaczky Mar 04 '15 at 13:10
  • about the first occurrence: why your model has initially a null value, angularjs reflect this creating an extra – Joao Polo Mar 04 '15 at 14:08
  • about the second occurrence... You should not use setTimeout on angularjs directly. You could use $timeout. I change your example using $timeout, and it works better :-) http://plnkr.co/edit/oF3MAJo9CJU0WmucVGoG?p=preview – Joao Polo Mar 04 '15 at 14:15
  • 1
    Yes, this was intentional, the point was to do something outside of Angular's scope, to demonstrate Angular's inconsistent handling of a simple array and an array of objects. Probably there are better showcases, but your demo helped me to narrow down this phenomenon! Thanks for your input! :) – orszaczky Mar 04 '15 at 15:05
0

Probably late, but here's a solution that helped me. I always add a virtual invisible option which is last in the list and never gets selected.

<select class="form-control" 
        ng-options="r as r for r in myList | orderBy track by r"
        ng-model="item">
    <option ng-show="false"></option> <!-- TRICK: select last value too -->
</select>
michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63