diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js index 5f67c950e7e1..6d776fb87ac6 100644 --- a/src/ng/directive/ngOptions.js +++ b/src/ng/directive/ngOptions.js @@ -325,13 +325,25 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) { // The option values were already computed in the `getWatchables` fn, // which must have been called to trigger `getOptions` var optionValues = valuesFn(scope) || []; + var optionValuesKeys; - var keys = Object.keys(optionValues); - keys.forEach(function getOption(key) { - // Ignore "angular" properties that start with $ or $$ - if (key.charAt(0) === '$') return; + if (!keyName && isArrayLike(optionValues)) { + optionValuesKeys = optionValues; + } else { + // if object, extract keys, in enumeration order, unsorted + optionValuesKeys = []; + for (var itemKey in optionValues) { + if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') { + optionValuesKeys.push(itemKey); + } + } + } + var optionValuesLength = optionValuesKeys.length; + + for (var index = 0; index < optionValuesLength; index++) { + var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index]; var value = optionValues[key]; var locals = getLocals(value, key); var viewValue = viewValueFn(scope, locals); @@ -343,7 +355,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) { optionItems.push(optionItem); selectValueMap[selectValue] = optionItem; - }); + } return { items: optionItems, diff --git a/test/ng/directive/ngOptionsSpec.js b/test/ng/directive/ngOptionsSpec.js index 2998ad7aa577..d0edd7be8e76 100644 --- a/test/ng/directive/ngOptionsSpec.js +++ b/test/ng/directive/ngOptionsSpec.js @@ -180,6 +180,47 @@ describe('ngOptions', function() { }); + it('should not include properties with non-numeric keys in array-like collections when using array syntax', function() { + createSelect({ + 'ng-model':'selected', + 'ng-options':'value for value in values' + }); + + scope.$apply(function() { + scope.values = { 0: 'X', 1: 'Y', 2: 'Z', 'a': 'A', length: 3}; + scope.selected = scope.values[1]; + }); + + var options = element.find('option'); + expect(options.length).toEqual(3); + expect(options.eq(0)).toEqualOption('X'); + expect(options.eq(1)).toEqualOption('Y'); + expect(options.eq(2)).toEqualOption('Z'); + + }); + + + it('should include properties with non-numeric keys in array-like collections when using object syntax', function() { + createSelect({ + 'ng-model':'selected', + 'ng-options':'value for (key, value) in values' + }); + + scope.$apply(function() { + scope.values = { 0: 'X', 1: 'Y', 2: 'Z', 'a': 'A', length: 3}; + scope.selected = scope.values[1]; + }); + + var options = element.find('option'); + expect(options.length).toEqual(5); + expect(options.eq(0)).toEqualOption('X'); + expect(options.eq(1)).toEqualOption('Y'); + expect(options.eq(2)).toEqualOption('Z'); + expect(options.eq(3)).toEqualOption('A'); + expect(options.eq(4)).toEqualOption(3); + }); + + it('should render an object', function() { createSelect({ 'ng-model': 'selected',