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(); + + })); });