Search code examples
angularjsng-options

Angular 1.3.3 ng-options issues, worked in 1.3.0


With AngularJS 1.3.0, this was working great for me:

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope) {
  $scope.setting = {
    active: 1,
    array: [{
      name: 'Small',
      value: 'small'
    }, {
      name: 'Medium',
      value: 'medium'
    }, {
      name: 'Large',
      value: 'large'
    }]
  }
});
<!DOCTYPE html>
<html ng-app="myApp">

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script>
</head>

<body ng-controller="MainCtrl">
  <select ng-model="setting.active" ng-options="i as FS.name || FS.value for (i, FS) in setting.array"></select>
</body>

</html>

On Angular 1.3.0, I was getting just those three options returned, with the "active" one selected. Now on 1.3.3, I'm getting that empty <option value="?"> tag.

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope) {
  $scope.setting = {
    active: 1,
    array: [{
      name: 'Small',
      value: 'small'
    }, {
      name: 'Medium',
      value: 'medium'
    }, {
      name: 'Large',
      value: 'large'
    }]
  }
});
<!DOCTYPE html>
<html ng-app="myApp">

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script>
</head>

<body ng-controller="MainCtrl">
  <select ng-model="setting.active" ng-options="i as FS.name || FS.value for (i, FS) in setting.array"></select>
</body>

</html>

Any ideas on how to change change ng-options to get it to work correctly again?

Thanks!


Solution

  • The problem

    Notice that you're using the version of the ngOptions parameter normally used for object data sources, even if your source is an array. Therefore, AngularJS assume it can safely treat it as an object, especially in order to get the keys put in the i variable. And it's indeed what is done by calling the internal sortedKeys() function, defined here:

    function sortedKeys(obj) {
        return Object.keys(obj).sort();
    }
    

    It's not a big deal, arrays can easily be converted in objects. But if the keys of arrays are numeric, keys of objects are strings. Thus, the i variable is a string too!

    That was not a problem before, because comparisons with the selectAs part was not strict, and 42 == '42', for instance. But that's not the case any more since this change in AngularJS 1.3.2.

    The solution

    Besides modifying yours objects to include the numeric key and use it in the selectAs part, you've got two simple solutions to go around the problem. The first is to use a string in your model, as you've suggested:

    $scope.setting.active = '1';
    

    The other one is to force the selectAs part to be numeric, for example by multiply it by 1:

    <select ng-model="setting.active" ng-options="i*1 as FS.name || FS.value for (i, FS) in setting.array"></select>