Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit eb3661c

Browse files
committed
perf(ngOptions): prevent initial options repaiting
Avoid double execution of `updateOptions()` method, which causes a complete repainting of all `<option>` elements. Fixes #15801
1 parent 0c3620b commit eb3661c

File tree

1 file changed

+43
-48
lines changed

1 file changed

+43
-48
lines changed

src/ng/directive/ngOptions.js

+43-48
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,9 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
428428
}
429429
}
430430

431+
// Remove all the elements inside the select.
432+
selectElement.empty();
433+
431434
var providedEmptyOption = !!selectCtrl.emptyOption;
432435

433436
var unknownOption = jqLite(optionTemplate.cloneNode(false));
@@ -449,36 +452,38 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
449452
if (!multiple) {
450453

451454
selectCtrl.writeValue = function writeNgOptionsValue(value) {
452-
var selectedOption = options.selectValueMap[selectElement.val()];
453-
var option = options.getOptionFromViewValue(value);
455+
if (options) {
456+
var selectedOption = options.selectValueMap[selectElement.val()];
457+
var option = options.getOptionFromViewValue(value);
454458

455-
// Make sure to remove the selected attribute from the previously selected option
456-
// Otherwise, screen readers might get confused
457-
if (selectedOption) selectedOption.element.removeAttribute('selected');
459+
// Make sure to remove the selected attribute from the previously selected option
460+
// Otherwise, screen readers might get confused
461+
if (selectedOption) selectedOption.element.removeAttribute('selected');
458462

459-
if (option) {
460-
// Don't update the option when it is already selected.
461-
// For example, the browser will select the first option by default. In that case,
462-
// most properties are set automatically - except the `selected` attribute, which we
463-
// set always
463+
if (option) {
464+
// Don't update the option when it is already selected.
465+
// For example, the browser will select the first option by default. In that case,
466+
// most properties are set automatically - except the `selected` attribute, which we
467+
// set always
464468

465-
if (selectElement[0].value !== option.selectValue) {
466-
selectCtrl.removeUnknownOption();
467-
selectCtrl.unselectEmptyOption();
468-
469-
selectElement[0].value = option.selectValue;
470-
option.element.selected = true;
471-
}
469+
if (selectElement[0].value !== option.selectValue) {
470+
selectCtrl.removeUnknownOption();
471+
selectCtrl.unselectEmptyOption();
472472

473-
option.element.setAttribute('selected', 'selected');
474-
} else {
473+
selectElement[0].value = option.selectValue;
474+
option.element.selected = true;
475+
}
475476

476-
if (providedEmptyOption) {
477-
selectCtrl.selectEmptyOption();
478-
} else if (selectCtrl.unknownOption.parent().length) {
479-
selectCtrl.updateUnknownOption(value);
477+
option.element.setAttribute('selected', 'selected');
480478
} else {
481-
selectCtrl.renderUnknownOption(value);
479+
480+
if (providedEmptyOption) {
481+
selectCtrl.selectEmptyOption();
482+
} else if (selectCtrl.unknownOption.parent().length) {
483+
selectCtrl.updateUnknownOption(value);
484+
} else {
485+
selectCtrl.renderUnknownOption(value);
486+
}
482487
}
483488
}
484489
};
@@ -508,16 +513,18 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
508513
} else {
509514

510515
selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
511-
// Only set `<option>.selected` if necessary, in order to prevent some browsers from
512-
// scrolling to `<option>` elements that are outside the `<select>` element's viewport.
516+
if (options) {
517+
// Only set `<option>.selected` if necessary, in order to prevent some browsers from
518+
// scrolling to `<option>` elements that are outside the `<select>` element's viewport.
513519

514-
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
520+
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
515521

516-
options.items.forEach(function(option) {
517-
if (option.element.selected && !includes(selectedOptions, option)) {
518-
option.element.selected = false;
519-
}
520-
});
522+
options.items.forEach(function(option) {
523+
if (option.element.selected && !includes(selectedOptions, option)) {
524+
option.element.selected = false;
525+
}
526+
});
527+
}
521528
};
522529

523530

@@ -552,13 +559,12 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
552559

553560
if (providedEmptyOption) {
554561

555-
// we need to remove it before calling selectElement.empty() because otherwise IE will
556-
// remove the label from the element. wtf?
557-
selectCtrl.emptyOption.remove();
558-
559562
// compile the element since there might be bindings in it
560563
$compile(selectCtrl.emptyOption)(scope);
561564

565+
// Ensure that the empty option is always there if it was explicitly provided
566+
selectElement.prepend(selectCtrl.emptyOption);
567+
562568
if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
563569
// This means the empty option has currently no actual DOM node, probably because
564570
// it has been modified by a transclusion directive.
@@ -590,12 +596,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
590596

591597
}
592598

593-
selectElement.empty();
594-
595-
// We need to do this here to ensure that the options object is defined
596-
// when we first hit it in writeNgOptionsValue
597-
updateOptions();
598-
599599
// We will re-render the option elements if the option values or labels change
600600
scope.$watchCollection(ngOptions.getWatchables, updateOptions);
601601

@@ -624,7 +624,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
624624
// the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
625625
// See https://github.com/angular/angular.js/issues/11314 for more info.
626626
// This is unfortunately untestable with unit / e2e tests
627-
if (option.label !== element.label) {
627+
if (option.label !== undefined) {
628628
element.label = option.label;
629629
element.textContent = option.label;
630630
}
@@ -655,11 +655,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
655655

656656
var groupElementMap = {};
657657

658-
// Ensure that the empty option is always there if it was explicitly provided
659-
if (providedEmptyOption) {
660-
selectElement.prepend(selectCtrl.emptyOption);
661-
}
662-
663658
options.items.forEach(function addOption(option) {
664659
var groupElement;
665660

0 commit comments

Comments
 (0)