diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js
index a8adfb0b2e1b..ad199406d694 100644
--- a/src/ng/directive/ngOptions.js
+++ b/src/ng/directive/ngOptions.js
@@ -429,6 +429,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
var providedEmptyOption = !!emptyOption;
+ var emptyOptionRendered = false;
var unknownOption = jqLite(optionTemplate.cloneNode(false));
unknownOption.val('?');
@@ -445,14 +446,16 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
selectElement.prepend(emptyOption);
}
selectElement.val('');
- emptyOption.prop('selected', true); // needed for IE
- emptyOption.attr('selected', true);
+ if (emptyOptionRendered) {
+ emptyOption.prop('selected', true); // needed for IE
+ emptyOption.attr('selected', true);
+ }
};
var removeEmptyOption = function() {
if (!providedEmptyOption) {
emptyOption.remove();
- } else {
+ } else if (emptyOptionRendered) {
emptyOption.removeAttr('selected');
}
};
@@ -587,9 +590,35 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
// compile the element since there might be bindings in it
$compile(emptyOption)(scope);
- // remove the class, which is added automatically because we recompile the element and it
- // becomes the compilation root
- emptyOption.removeClass('ng-scope');
+ if (emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
+ // This means the empty option has currently no actual DOM node, probably because
+ // it has been modified by a transclusion directive.
+
+ emptyOptionRendered = false;
+
+ // Redefine the registerOption function, which will catch
+ // options that are added by ngIf etc. (rendering of the node is async because of
+ // lazy transclusion)
+ selectCtrl.registerOption = function(optionScope, optionEl) {
+ if (optionEl.val() === '') {
+ emptyOptionRendered = true;
+ emptyOption = optionEl;
+ emptyOption.removeClass('ng-scope');
+ // This ensures the new empty option is selected if previously no option was selected
+ ngModelCtrl.$render();
+
+ optionEl.on('$destroy', function() {
+ emptyOption = undefined;
+ emptyOptionRendered = false;
+ });
+ }
+ };
+
+ } else {
+ emptyOption.removeClass('ng-scope');
+ emptyOptionRendered = true;
+ }
+
} else {
emptyOption = jqLite(optionTemplate.cloneNode(false));
}
diff --git a/test/ng/directive/ngOptionsSpec.js b/test/ng/directive/ngOptionsSpec.js
index edfc0f98d108..f1f752bf352d 100644
--- a/test/ng/directive/ngOptionsSpec.js
+++ b/test/ng/directive/ngOptionsSpec.js
@@ -2558,6 +2558,95 @@ describe('ngOptions', function() {
});
+ it('should select the correct option after linking when the ngIf expression is initially falsy', function() {
+ scope.values = [
+ {name:'black'},
+ {name:'white'},
+ {name:'red'}
+ ];
+ scope.selected = scope.values[2];
+
+ expect(function() {
+ createSingleSelect('');
+ scope.$apply();
+ }).not.toThrow();
+
+ expect(element.find('option')[2]).toBeMarkedAsSelected();
+ expect(linkLog).toEqual(['linkNgOptions']);
+ });
+
+
+ it('should add / remove the "selected" attribute on empty option which has an initially falsy ngIf expression', function() {
+ scope.values = [
+ {name:'black'},
+ {name:'white'},
+ {name:'red'}
+ ];
+ scope.selected = scope.values[2];
+
+ createSingleSelect('');
+ scope.$apply();
+
+ expect(element.find('option')[2]).toBeMarkedAsSelected();
+
+ scope.$apply('isBlank = true');
+ expect(element.find('option')[0].value).toBe('');
+ expect(element.find('option')[0]).not.toBeMarkedAsSelected();
+
+ scope.$apply('selected = null');
+ expect(element.find('option')[0].value).toBe('');
+ expect(element.find('option')[0]).toBeMarkedAsSelected();
+
+ scope.selected = scope.values[1];
+ scope.$apply();
+ expect(element.find('option')[0].value).toBe('');
+ expect(element.find('option')[0]).not.toBeMarkedAsSelected();
+ expect(element.find('option')[2]).toBeMarkedAsSelected();
+ });
+
+
+ it('should add / remove the "selected" attribute on empty option which has an initially truthy ngIf expression when no option is selected', function() {
+ scope.values = [
+ {name:'black'},
+ {name:'white'},
+ {name:'red'}
+ ];
+ scope.isBlank = true;
+
+ createSingleSelect('');
+ scope.$apply();
+
+ expect(element.find('option')[0].value).toBe('');
+ expect(element.find('option')[0]).toBeMarkedAsSelected();
+
+ scope.selected = scope.values[2];
+ scope.$apply();
+ expect(element.find('option')[0]).not.toBeMarkedAsSelected();
+ expect(element.find('option')[3]).toBeMarkedAsSelected();
+ });
+
+
+ it('should add the "selected" attribute on empty option which has an initially falsy ngIf expression when no option is selected', function() {
+ scope.values = [
+ {name:'black'},
+ {name:'white'},
+ {name:'red'}
+ ];
+
+ createSingleSelect('');
+ scope.$apply();
+
+ expect(element.find('option')[0]).not.toBeMarkedAsSelected();
+
+ scope.isBlank = true;
+ scope.$apply();
+
+ expect(element.find('option')[0].value).toBe('');
+ expect(element.find('option')[0]).toBeMarkedAsSelected();
+ expect(element.find('option')[1]).not.toBeMarkedAsSelected();
+ });
+
+
it('should not throw with a directive that replaces', inject(function($templateCache, $httpBackend) {
$templateCache.put('select_template.html', '');