diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js
index 282dff3aedd2..561061b07020 100644
--- a/src/ng/directive/ngOptions.js
+++ b/src/ng/directive/ngOptions.js
@@ -395,6 +395,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
return {
restrict: 'A',
terminal: true,
+ controller: function() { },
require: ['select', 'ngModel'],
link: function(scope, selectElement, attr, ctrls) {
diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js
index df2bcee32437..2f9aca5db9fc 100644
--- a/src/ng/directive/select.js
+++ b/src/ng/directive/select.js
@@ -417,10 +417,15 @@ var optionDirective = ['$interpolate', function($interpolate) {
// This is an optimization over using ^^ since we don't want to have to search
// all the way to the root of the DOM for every single option element
- var selectCtrlName = '$selectController',
- parent = element.parent(),
- selectCtrl = parent.data(selectCtrlName) ||
- parent.parent().data(selectCtrlName); // in case we are in optgroup
+ var selectElement = element.parent();
+ if (nodeName_(selectElement) !== 'select') {
+ selectElement = selectElement.parent(); // in case we are in optgroup
+ }
+
+ var selectCtrl = selectElement.data('$selectController');
+
+ // If there is an ngOptions directive then bail out
+ if (selectElement.data('$ngOptionsController')) return;
function addOption(optionValue) {
selectCtrl.addOption(optionValue, element);
diff --git a/test/ng/directive/ngOptionsSpec.js b/test/ng/directive/ngOptionsSpec.js
index bc50bcbef818..4a5361352381 100644
--- a/test/ng/directive/ngOptionsSpec.js
+++ b/test/ng/directive/ngOptionsSpec.js
@@ -104,6 +104,30 @@ describe('ngOptions', function() {
});
});
+ beforeEach(module(function($compileProvider) {
+ $compileProvider
+ .directive('compileContents', function($compile) {
+ return {
+ link: {
+ pre: function(scope, element) {
+ $compile(element.contents())(scope);
+ }
+ }
+ };
+ })
+ .directive('customSelect', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ scope: { ngModel: '=', options: '='},
+ templateUrl: 'select_template.html',
+ link: function(scope) {
+ scope.selectable_options = scope.options;
+ }
+ }
+ });
+ }));
+
beforeEach(inject(function($rootScope, _$compile_) {
scope = $rootScope.$new(); //create a child scope because the root scope can't be $destroy-ed
$compile = _$compile_;
@@ -2119,6 +2143,31 @@ describe('ngOptions', function() {
option = element.find('option').eq(0);
expect(option.text()).toBe('A');
});
+
+
+ it('should not throw when a directive compiles the blank option before ngOptions is linked', function() {
+ expect(function() {
+ createSelect({
+ 'compile-contents': '',
+ 'name': 'select',
+ 'ng-model': 'value',
+ 'ng-options': 'item for item in items',
+ }, true);
+ }).not.toThrow();
+ });
+
+ it('should not throw with a directive that replaces', inject(function($templateCache, $httpBackend) {
+ $templateCache.put('select_template.html', '');
+
+ scope.options = ['a', 'b', 'c', 'd'];
+
+ expect(function() {
+ var element = $compile('')(scope);
+ scope.$digest();
+ dealoc(element);
+ }).not.toThrow();
+
+ }));
});