Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit dfa722a

Browse files
fix(ngOptions): iterate over the options collection in the same way as ngRepeat
In `ngRepeat` if the object to be iterated over is "array-like" then it only iterates over the numerical indexes rather than every key on the object. This prevents "helper" methods from being included in the rendered collection. This commit changes `ngOptions` to iterate in the same way. BREAKING CHANGE: Although it is unlikely that anyone is using it in this way, this change does change the behaviour of `ngOptions` in the following case: * you are iterating over an array-like object, using the array form of the `ngOptions` syntax (`item.label for item in items`) and that object contains non-numeric property keys. In this case these properties with non-numeric keys will be ignored. ** Here array-like is defined by the result of a call to this internal function: https://github.com/angular/angular.js/blob/v1.4.0-rc.1/src/Angular.js#L198-L211 ** To get the desired behaviour you need to iterate using the object form of the `ngOptions` syntax (`value.label` for (key, value) in items)`). Closes #11733
1 parent cc96188 commit dfa722a

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

src/ng/directive/ngOptions.js

+17-5
Original file line numberDiff line numberDiff line change
@@ -325,13 +325,25 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
325325
// The option values were already computed in the `getWatchables` fn,
326326
// which must have been called to trigger `getOptions`
327327
var optionValues = valuesFn(scope) || [];
328+
var optionValuesKeys;
328329

329-
var keys = Object.keys(optionValues);
330-
keys.forEach(function getOption(key) {
331330

332-
// Ignore "angular" properties that start with $ or $$
333-
if (key.charAt(0) === '$') return;
331+
if (!keyName && isArrayLike(optionValues)) {
332+
optionValuesKeys = optionValues;
333+
} else {
334+
// if object, extract keys, in enumeration order, unsorted
335+
optionValuesKeys = [];
336+
for (var itemKey in optionValues) {
337+
if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
338+
optionValuesKeys.push(itemKey);
339+
}
340+
}
341+
}
334342

343+
var optionValuesLength = optionValuesKeys.length;
344+
345+
for (var index = 0; index < optionValuesLength; index++) {
346+
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
335347
var value = optionValues[key];
336348
var locals = getLocals(value, key);
337349
var viewValue = viewValueFn(scope, locals);
@@ -343,7 +355,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
343355

344356
optionItems.push(optionItem);
345357
selectValueMap[selectValue] = optionItem;
346-
});
358+
}
347359

348360
return {
349361
items: optionItems,

test/ng/directive/ngOptionsSpec.js

+41
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,47 @@ describe('ngOptions', function() {
180180
});
181181

182182

183+
it('should not include properties with non-numeric keys in array-like collections when using array syntax', function() {
184+
createSelect({
185+
'ng-model':'selected',
186+
'ng-options':'value for value in values'
187+
});
188+
189+
scope.$apply(function() {
190+
scope.values = { 0: 'X', 1: 'Y', 2: 'Z', 'a': 'A', length: 3};
191+
scope.selected = scope.values[1];
192+
});
193+
194+
var options = element.find('option');
195+
expect(options.length).toEqual(3);
196+
expect(options.eq(0)).toEqualOption('X');
197+
expect(options.eq(1)).toEqualOption('Y');
198+
expect(options.eq(2)).toEqualOption('Z');
199+
200+
});
201+
202+
203+
it('should include properties with non-numeric keys in array-like collections when using object syntax', function() {
204+
createSelect({
205+
'ng-model':'selected',
206+
'ng-options':'value for (key, value) in values'
207+
});
208+
209+
scope.$apply(function() {
210+
scope.values = { 0: 'X', 1: 'Y', 2: 'Z', 'a': 'A', length: 3};
211+
scope.selected = scope.values[1];
212+
});
213+
214+
var options = element.find('option');
215+
expect(options.length).toEqual(5);
216+
expect(options.eq(0)).toEqualOption('X');
217+
expect(options.eq(1)).toEqualOption('Y');
218+
expect(options.eq(2)).toEqualOption('Z');
219+
expect(options.eq(3)).toEqualOption('A');
220+
expect(options.eq(4)).toEqualOption(3);
221+
});
222+
223+
183224
it('should render an object', function() {
184225
createSelect({
185226
'ng-model': 'selected',

0 commit comments

Comments
 (0)