diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js
index 886aae20306a..91585934445d 100644
--- a/src/ng/directive/select.js
+++ b/src/ng/directive/select.js
@@ -405,21 +405,35 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
value = valueFn(scope, locals);
}
}
- // Update the null option's selected property here so $render cleans it up correctly
- if (optionGroupsCache[0].length > 1) {
- if (optionGroupsCache[0][1].id !== key) {
- optionGroupsCache[0][1].selected = false;
- }
- }
}
ctrl.$setViewValue(value);
+ render();
});
});
ctrl.$render = render;
- // TODO(vojta): can't we optimize this ?
- scope.$watch(render);
+ scope.$watch(valuesFn, render, true);
+ scope.$watch(getSelectedSet, render, true);
+
+ function getSelectedSet() {
+ var selectedSet = false;
+ if (multiple) {
+ var modelValue = ctrl.$modelValue;
+ if (trackFn && isArray(modelValue)) {
+ selectedSet = new HashMap([]);
+ var locals = {};
+ for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
+ locals[valueName] = modelValue[trackIndex];
+ selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
+ }
+ } else {
+ selectedSet = new HashMap(modelValue);
+ }
+ }
+ return selectedSet;
+ }
+
function render() {
// Temporary location for the option groups before we render them
@@ -437,22 +451,11 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
groupIndex, index,
locals = {},
selected,
- selectedSet = false, // nothing is selected yet
+ selectedSet = getSelectedSet(),
lastElement,
element,
label;
- if (multiple) {
- if (trackFn && isArray(modelValue)) {
- selectedSet = new HashMap([]);
- for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
- locals[valueName] = modelValue[trackIndex];
- selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
- }
- } else {
- selectedSet = new HashMap(modelValue);
- }
- }
// We now build up the list of options we need (we merge later)
for (index = 0; length = keys.length, index < length; index++) {
@@ -548,7 +551,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
lastElement.val(existingOption.id = option.id);
}
// lastElement.prop('selected') provided by jQuery has side-effects
- if (existingOption.selected !== option.selected) {
+ if (lastElement[0].selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected));
if (msie) {
// See #7692
diff --git a/test/ng/directive/selectSpec.js b/test/ng/directive/selectSpec.js
index 9ce01704c136..3261605ffbde 100644
--- a/test/ng/directive/selectSpec.js
+++ b/test/ng/directive/selectSpec.js
@@ -735,6 +735,8 @@ describe('select', function() {
it('should not update selected property of an option element on digest with no change event',
function() {
+ // ng-options="value.name for value in values"
+ // ng-model="selected"
createSingleSelect();
scope.$apply(function() {
@@ -743,6 +745,11 @@ describe('select', function() {
});
var options = element.find('option');
+
+ expect(scope.selected).toEqual({ name: 'A' });
+ expect(options.eq(0).prop('selected')).toBe(true);
+ expect(options.eq(1).prop('selected')).toBe(false);
+
var optionToSelect = options.eq(1);
expect(optionToSelect.text()).toBe('B');
@@ -1148,8 +1155,73 @@ describe('select', function() {
browserTrigger(element, 'change');
expect(scope.selected).toEqual(null);
});
+
+
+ // Regression https://github.com/angular/angular.js/issues/7855
+ it('should update the model with ng-change', function() {
+ createSelect({
+ 'ng-change':'change()',
+ 'ng-model':'selected',
+ 'ng-options':'value for value in values'
+ });
+
+ scope.$apply(function() {
+ scope.values = ['A', 'B'];
+ scope.selected = 'A';
+ });
+
+ scope.change = function() {
+ scope.selected = 'A';
+ };
+
+ element.find('option')[1].selected = true;
+
+ browserTrigger(element, 'change');
+ expect(element.find('option')[0].selected).toBeTruthy();
+ expect(scope.selected).toEqual('A');
+ });
});
+ describe('disabled blank', function() {
+ it('should select disabled blank by default', function() {
+ var html = '';
+ scope.$apply(function() {
+ scope.choices = ['A', 'B', 'C'];
+ });
+
+ compile(html);
+
+ var options = element.find('option');
+ var optionToSelect = options.eq(0);
+ expect(optionToSelect.text()).toBe('Choose One');
+ expect(optionToSelect.prop('selected')).toBe(true);
+ expect(element[0].value).toBe('');
+
+ dealoc(element);
+ });
+
+
+ it('should select disabled blank by default when select is required', function() {
+ var html = '';
+ scope.$apply(function() {
+ scope.choices = ['A', 'B', 'C'];
+ });
+
+ compile(html);
+
+ var options = element.find('option');
+ var optionToSelect = options.eq(0);
+ expect(optionToSelect.text()).toBe('Choose One');
+ expect(optionToSelect.prop('selected')).toBe(true);
+ expect(element[0].value).toBe('');
+
+ dealoc(element);
+ });
+ });
describe('select-many', function() {
@@ -1197,6 +1269,7 @@ describe('select', function() {
expect(scope.selected).toEqual([scope.values[0]]);
});
+
it('should select from object', function() {
createSelect({
'ng-model':'selected',