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

Commit abf59c2

Browse files
slavedepetebacondarwin
authored andcommitted
fix(select): allow empty option to be added dynamically by ng-repeat
The select directive supports provision of an "empty" element that is used if the value of the select is undefined. This fix ensures that this empty option can be provided dynamically after the initial compilation has completed. Closes #11470 Closes #11512
1 parent 8914f8e commit abf59c2

File tree

3 files changed

+81
-15
lines changed

3 files changed

+81
-15
lines changed

src/ng/directive/ngOptions.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,16 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
370370
var selectCtrl = ctrls[0];
371371
var multiple = attr.multiple;
372372

373-
var emptyOption = selectCtrl.emptyOption;
373+
// The emptyOption allows the application developer to provide their own custom "empty"
374+
// option when the viewValue does not match any of the option values.
375+
var emptyOption;
376+
for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
377+
if (children[i].value === '') {
378+
emptyOption = children.eq(i);
379+
break;
380+
}
381+
}
382+
374383
var providedEmptyOption = !!emptyOption;
375384

376385
var unknownOption = jqLite(optionTemplate.cloneNode(false));

src/ng/directive/select.js

+7-14
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,6 @@ var SelectController =
4242
if (self.unknownOption.parent()) self.unknownOption.remove();
4343
};
4444

45-
// Here we find the option that represents the "empty" value, i.e. the option with a value
46-
// of `""`. This option needs to be accessed (to select it directly) when setting the value
47-
// of the select to `""` because IE9 will not automatically select the option.
48-
//
49-
// Additionally, the `ngOptions` directive uses this option to allow the application developer
50-
// to provide their own custom "empty" option when the viewValue does not match any of the
51-
// option values.
52-
for (var i = 0, children = $element.children(), ii = children.length; i < ii; i++) {
53-
if (children[i].value === '') {
54-
self.emptyOption = children.eq(i);
55-
break;
56-
}
57-
}
5845

5946
// Read the value of the select control, the implementation of this changes depending
6047
// upon whether the select can have multiple values and whether ngOptions is at work.
@@ -83,8 +70,11 @@ var SelectController =
8370

8471

8572
// Tell the select control that an option, with the given value, has been added
86-
self.addOption = function(value) {
73+
self.addOption = function(value, element) {
8774
assertNotHasOwnProperty(value, '"option value"');
75+
if (value === '') {
76+
self.emptyOption = element;
77+
}
8878
var count = optionsMap.get(value) || 0;
8979
optionsMap.put(value, count + 1);
9080
};
@@ -95,6 +85,9 @@ var SelectController =
9585
if (count) {
9686
if (count === 1) {
9787
optionsMap.remove(value);
88+
if (value === '') {
89+
self.emptyOption = undefined;
90+
}
9891
} else {
9992
optionsMap.put(value, count - 1);
10093
}

test/ng/directive/selectSpec.js

+64
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,70 @@ describe('select', function() {
200200

201201
describe('empty option', function() {
202202

203+
it('should allow empty option to be added and removed dynamically', function() {
204+
205+
scope.dynamicOptions = [];
206+
scope.robot = '';
207+
compile('<select ng-model="robot">' +
208+
'<option ng-repeat="opt in dynamicOptions" value="{{opt.val}}">{{opt.display}}</option>' +
209+
'</selec>');
210+
expect(element).toEqualSelect(['? string: ?']);
211+
212+
213+
scope.dynamicOptions = [
214+
{ val: '', display: '--select--' },
215+
{ val: 'x', display: 'robot x' },
216+
{ val: 'y', display: 'robot y' }
217+
];
218+
scope.$digest();
219+
expect(element).toEqualSelect([''], 'x', 'y');
220+
221+
222+
scope.robot = 'x';
223+
scope.$digest();
224+
expect(element).toEqualSelect('', ['x'], 'y');
225+
226+
227+
scope.dynamicOptions.shift();
228+
scope.$digest();
229+
expect(element).toEqualSelect(['x'], 'y');
230+
231+
232+
scope.robot = undefined;
233+
scope.$digest();
234+
expect(element).toEqualSelect([unknownValue(undefined)], 'x', 'y');
235+
});
236+
237+
238+
it('should cope with a dynamic empty option added to a static empty option', function() {
239+
scope.dynamicOptions = [];
240+
scope.robot = 'x';
241+
compile('<select ng-model="robot">' +
242+
'<option value="">--static-select--</option>' +
243+
'<option ng-repeat="opt in dynamicOptions" value="{{opt.val}}">{{opt.display}}</option>' +
244+
'</selec>');
245+
scope.$digest();
246+
expect(element).toEqualSelect([unknownValue('x')], '');
247+
248+
scope.robot = undefined;
249+
scope.$digest();
250+
expect(element.find('option').eq(0).prop('selected')).toBe(true);
251+
expect(element.find('option').eq(0).text()).toBe('--static-select--');
252+
253+
scope.dynamicOptions = [
254+
{ val: '', display: '--dynamic-select--' },
255+
{ val: 'x', display: 'robot x' },
256+
{ val: 'y', display: 'robot y' }
257+
];
258+
scope.$digest();
259+
expect(element).toEqualSelect([''], '', 'x', 'y');
260+
261+
262+
scope.dynamicOptions = [];
263+
scope.$digest();
264+
expect(element).toEqualSelect(['']);
265+
});
266+
203267
it('should select the empty option when model is undefined', function() {
204268
compile('<select ng-model="robot">' +
205269
'<option value="">--select--</option>' +

0 commit comments

Comments
 (0)