diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js
index b276fddb461d..af963c665dc7 100644
--- a/src/ng/directive/ngOptions.js
+++ b/src/ng/directive/ngOptions.js
@@ -453,6 +453,9 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
if (!multiple) {
selectCtrl.writeValue = function writeNgOptionsValue(value) {
+ // The options might not be defined yet when ngModel tries to render
+ if (!options) return;
+
var selectedOption = selectElement[0].options[selectElement[0].selectedIndex];
var option = options.getOptionFromViewValue(value);
@@ -504,9 +507,11 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
} else {
selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
+ // The options might not be defined yet when ngModel tries to render
+ if (!options) return;
+
// Only set `.selected` if necessary, in order to prevent some browsers from
// scrolling to ` ` elements that are outside the `` element's viewport.
-
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
options.items.forEach(function(option) {
@@ -588,10 +593,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
- // We need to do this here to ensure that the options object is defined
- // when we first hit it in writeNgOptionsValue
- updateOptions();
-
// We will re-render the option elements if the option values or labels change
scope.$watchCollection(ngOptions.getWatchables, updateOptions);
diff --git a/test/ng/directive/ngOptionsSpec.js b/test/ng/directive/ngOptionsSpec.js
index 6c4bbaab5a33..1ab5d0a91889 100644
--- a/test/ng/directive/ngOptionsSpec.js
+++ b/test/ng/directive/ngOptionsSpec.js
@@ -2,7 +2,7 @@
describe('ngOptions', function() {
- var scope, formElement, element, $compile, linkLog, ngModelCtrl;
+ var scope, formElement, element, $compile, linkLog, childListMutationObserver, ngModelCtrl;
function compile(html) {
formElement = jqLite('');
@@ -151,7 +151,18 @@ describe('ngOptions', function() {
$compile(element.contents())(scope);
}
};
- });
+ })
+
+ .directive('observeChildList', function() {
+ return {
+ link: function(scope, element) {
+ var config = { childList: true };
+
+ childListMutationObserver = new window.MutationObserver(noop);
+ childListMutationObserver.observe(element[0], config);
+ }
+ };
+ });
$provide.decorator('ngOptionsDirective', function($delegate) {
@@ -801,6 +812,29 @@ describe('ngOptions', function() {
expect(options[2]).toBeMarkedAsSelected();
});
+
+ if (window.MutationObserver) {
+ //IE9 and IE10 do not support MutationObserver
+ //Since the feature is only needed for a test, it's okay to skip these browsers
+ it('should render the initial options only one time', function() {
+ scope.value = 'black';
+ scope.values = ['black', 'white', 'red'];
+ // observe-child-list adds a MutationObserver that we will read out after ngOptions
+ // has been compiled
+ createSelect({
+ 'ng-model':'value',
+ 'ng-options':'value.name for value in values',
+ 'observe-child-list': ''
+ });
+
+ var optionEls = element[0].querySelectorAll('option');
+ var records = childListMutationObserver.takeRecords();
+
+ expect(records.length).toBe(1);
+ expect(records[0].addedNodes).toEqual(optionEls);
+ });
+ }
+
describe('disableWhen expression', function() {
describe('on single select', function() {
@@ -2966,6 +3000,30 @@ describe('ngOptions', function() {
optionsSetSelected[0].calls.reset();
optionsSetSelected[1].calls.reset();
});
+
+ if (window.MutationObserver) {
+ //IE9 and IE10 do not support MutationObserver
+ //Since the feature is only needed for a test, it's okay to skip these browsers
+ it('should render the initial options only one time', function() {
+ scope.value = ['black'];
+ scope.values = ['black', 'white', 'red'];
+ // observe-child-list adds a MutationObserver that we will read out after ngOptions
+ // has been compiled
+ createSelect({
+ 'ng-model':'selected',
+ 'ng-options':'value.name for value in values',
+ 'multiple': 'true',
+ 'observe-child-list': ''
+ });
+
+ var optionEls = element[0].querySelectorAll('option');
+ var records = childListMutationObserver.takeRecords();
+
+ expect(records.length).toBe(1);
+ expect(records[0].addedNodes).toEqual(optionEls);
+ });
+ }
+
});