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

Commit 860cb61

Browse files
committed
actual fix
1 parent 11d2b93 commit 860cb61

File tree

2 files changed

+107
-6
lines changed

2 files changed

+107
-6
lines changed

src/ng/directive/ngOptions.js

+35-6
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
429429
}
430430

431431
var providedEmptyOption = !!emptyOption;
432+
var emptyOptionRendered = false;
432433

433434
var unknownOption = jqLite(optionTemplate.cloneNode(false));
434435
unknownOption.val('?');
@@ -445,14 +446,16 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
445446
selectElement.prepend(emptyOption);
446447
}
447448
selectElement.val('');
448-
emptyOption.prop('selected', true); // needed for IE
449-
emptyOption.attr('selected', true);
449+
if (emptyOptionRendered) {
450+
emptyOption.prop('selected', true); // needed for IE
451+
emptyOption.attr('selected', true);
452+
}
450453
};
451454

452455
var removeEmptyOption = function() {
453456
if (!providedEmptyOption) {
454457
emptyOption.remove();
455-
} else if (emptyOption[0].nodeType === NODE_TYPE_ELEMENT) {
458+
} else if (emptyOptionRendered) {
456459
emptyOption.removeAttr('selected');
457460
}
458461
};
@@ -587,9 +590,35 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
587590
// compile the element since there might be bindings in it
588591
$compile(emptyOption)(scope);
589592

590-
// remove the class, which is added automatically because we recompile the element and it
591-
// becomes the compilation root
592-
emptyOption.removeClass('ng-scope');
593+
if (emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
594+
// This means the empty option has currently no actual DOM node, probably because
595+
// it has been modified by a transclusion directive.
596+
597+
emptyOptionRendered = false;
598+
599+
// Redefine the registerOption function, which will catch
600+
// options that are added by ngIf etc. (rendering of the node is async because of
601+
// lazy transclusion)
602+
selectCtrl.registerOption = function(optionScope, optionEl) {
603+
console.log('register');
604+
if (optionEl.val() === '') {
605+
emptyOptionRendered = true;
606+
emptyOption = optionEl;
607+
emptyOption.removeClass('ng-scope');
608+
// This ensures the new empty option is selected if previously no option was selected
609+
ngModelCtrl.$render();
610+
611+
optionEl.on('$destroy', function() {
612+
emptyOption = undefined;
613+
emptyOptionRendered = false;
614+
});
615+
}
616+
}
617+
} else {
618+
emptyOption.removeClass('ng-scope');
619+
emptyOptionRendered = true;
620+
}
621+
593622
} else {
594623
emptyOption = jqLite(optionTemplate.cloneNode(false));
595624
}

test/ng/directive/ngOptionsSpec.js

+72
Original file line numberDiff line numberDiff line change
@@ -2574,6 +2574,78 @@ describe('ngOptions', function() {
25742574
expect(linkLog).toEqual(['linkNgOptions']);
25752575
});
25762576

2577+
2578+
it('should add / remove the "selected" attribute on empty option which has an initially falsy ngIf expression', function() {
2579+
scope.values = [
2580+
{name:'black'},
2581+
{name:'white'},
2582+
{name:'red'}
2583+
];
2584+
scope.selected = scope.values[2];
2585+
2586+
createSingleSelect('<option ng-if="isBlank" value="">blank</option>');
2587+
scope.$apply();
2588+
2589+
expect(element.find('option')[2]).toBeMarkedAsSelected();
2590+
2591+
scope.$apply('isBlank = true');
2592+
expect(element.find('option')[0].value).toBe('');
2593+
expect(element.find('option')[0]).not.toBeMarkedAsSelected();
2594+
2595+
scope.$apply('selected = null');
2596+
expect(element.find('option')[0].value).toBe('');
2597+
expect(element.find('option')[0]).toBeMarkedAsSelected();
2598+
2599+
scope.selected = scope.values[1];
2600+
scope.$apply();
2601+
expect(element.find('option')[0].value).toBe('');
2602+
expect(element.find('option')[0]).not.toBeMarkedAsSelected();
2603+
expect(element.find('option')[2]).toBeMarkedAsSelected();
2604+
});
2605+
2606+
2607+
it('should add / remove the "selected" attribute on empty option which has an initially truthy ngIf expression when no option is selected', function() {
2608+
scope.values = [
2609+
{name:'black'},
2610+
{name:'white'},
2611+
{name:'red'}
2612+
];
2613+
scope.isBlank = true;
2614+
2615+
createSingleSelect('<option ng-if="isBlank" value="">blank</option>');
2616+
scope.$apply();
2617+
2618+
expect(element.find('option')[0].value).toBe('');
2619+
expect(element.find('option')[0]).toBeMarkedAsSelected();
2620+
2621+
scope.selected = scope.values[2];
2622+
scope.$apply();
2623+
expect(element.find('option')[0]).not.toBeMarkedAsSelected();
2624+
expect(element.find('option')[3]).toBeMarkedAsSelected();
2625+
});
2626+
2627+
2628+
it('should add the "selected" attribute on empty option which has an initially falsy ngIf expression when no option is selected', function() {
2629+
scope.values = [
2630+
{name:'black'},
2631+
{name:'white'},
2632+
{name:'red'}
2633+
];
2634+
2635+
createSingleSelect('<option ng-if="isBlank" value="">blank</option>');
2636+
scope.$apply();
2637+
2638+
expect(element.find('option')[0]).not.toBeMarkedAsSelected();
2639+
2640+
scope.isBlank = true;
2641+
scope.$apply();
2642+
2643+
expect(element.find('option')[0].value).toBe('');
2644+
expect(element.find('option')[0]).toBeMarkedAsSelected();
2645+
expect(element.find('option')[1]).not.toBeMarkedAsSelected();
2646+
});
2647+
2648+
25772649
it('should not throw with a directive that replaces', inject(function($templateCache, $httpBackend) {
25782650
$templateCache.put('select_template.html', '<select ng-options="option as option for option in selectable_options"> <option value="">This is a test</option> </select>');
25792651

0 commit comments

Comments
 (0)