diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js index 26f2f0cf1904..6ede818f68f0 100644 --- a/src/ng/directive/ngOptions.js +++ b/src/ng/directive/ngOptions.js @@ -401,6 +401,9 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) { // The emptyOption allows the application developer to provide their own custom "empty" // option when the viewValue does not match any of the option values. var emptyOption; + // The empty option might have a directive that dynamically adds / removes it + var dynamicEmptyOption; + for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) { if (children[i].value === '') { emptyOption = children.eq(i); @@ -550,6 +553,9 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) { // compile the element since there might be bindings in it $compile(emptyOption)(scope); + if (emptyOption[0].nodeType === NODE_TYPE_COMMENT) { + dynamicEmptyOption = true; + } // remove the class, which is added automatically because we recompile the element and it // becomes the compilation root @@ -619,9 +625,15 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) { var unknownOption_ = unknownOption && unknownOption[0]; if (emptyOption_ || unknownOption_) { - while (current && - (current === emptyOption_ || - current === unknownOption_)) { + + while ( + current && + (current === emptyOption_ || + current === unknownOption_ || + // When the empty option is dynamically added / removed, we cannot be sure that the + // emptyOption is the same as it was when it was extracted in the first place + dynamicEmptyOption && (current.nodeType === NODE_TYPE_COMMENT || current.value === '')) + ) { current = current.nextSibling; } } diff --git a/test/ng/directive/ngOptionsSpec.js b/test/ng/directive/ngOptionsSpec.js index c5dac4b72914..c0ea84a408e1 100644 --- a/test/ng/directive/ngOptionsSpec.js +++ b/test/ng/directive/ngOptionsSpec.js @@ -2144,6 +2144,53 @@ describe('ngOptions', function() { }); + it('should be possible to use ngIf in the blank option', function() { + var option; + createSingleSelect(''); + + scope.$apply(function() { + scope.values = [{name: 'A'}]; + scope.isBlank = true; + }); + + expect(element.find('option').length).toBe(2); + option = element.find('option').eq(0); + expect(option.val()).toBe(''); + expect(option.text()).toBe('blank'); + + scope.$apply(function() { + scope.isBlank = false; + }); + + expect(element.find('option').length).toBe(1); + option = element.find('option').eq(0); + expect(option.text()).toBe('A'); + }); + + + it('should be possible to use ngIf in the blank option when values are available upon linking', + function() { + var options; + + scope.values = [{name: 'A'}]; + createSingleSelect(''); + + scope.$apply('isBlank = true'); + + options = element.find('option'); + expect(options.length).toBe(2); + expect(options.eq(0).val()).toBe(''); + expect(options.eq(0).text()).toBe('blank'); + + scope.$apply('isBlank = false'); + + options = element.find('option'); + expect(options.length).toBe(1); + expect(options.eq(0).text()).toBe('A'); + } + ); + + it('should not throw when a directive compiles the blank option before ngOptions is linked', function() { expect(function() { createSelect({