Skip to content

Commit 0121ffa

Browse files
Merge pull request #15 from angular/master
Update upstream
2 parents a98295e + ff0e611 commit 0121ffa

File tree

7 files changed

+385
-115
lines changed

7 files changed

+385
-115
lines changed

src/ng/directive/ngOptions.js

+9-13
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,12 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
449449
if (!multiple) {
450450

451451
selectCtrl.writeValue = function writeNgOptionsValue(value) {
452-
var selectedOption = options.selectValueMap[selectElement.val()];
452+
var selectedOption = selectElement[0].options[selectElement[0].selectedIndex];
453453
var option = options.getOptionFromViewValue(value);
454454

455455
// Make sure to remove the selected attribute from the previously selected option
456456
// Otherwise, screen readers might get confused
457-
if (selectedOption) selectedOption.element.removeAttribute('selected');
457+
if (selectedOption) selectedOption.removeAttribute('selected');
458458

459459
if (option) {
460460
// Don't update the option when it is already selected.
@@ -464,22 +464,14 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
464464

465465
if (selectElement[0].value !== option.selectValue) {
466466
selectCtrl.removeUnknownOption();
467-
selectCtrl.unselectEmptyOption();
468467

469468
selectElement[0].value = option.selectValue;
470469
option.element.selected = true;
471470
}
472471

473472
option.element.setAttribute('selected', 'selected');
474473
} else {
475-
476-
if (providedEmptyOption) {
477-
selectCtrl.selectEmptyOption();
478-
} else if (selectCtrl.unknownOption.parent().length) {
479-
selectCtrl.updateUnknownOption(value);
480-
} else {
481-
selectCtrl.renderUnknownOption(value);
482-
}
474+
selectCtrl.selectUnknownOrEmptyOption(value);
483475
}
484476
};
485477

@@ -657,7 +649,12 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
657649

658650
// Ensure that the empty option is always there if it was explicitly provided
659651
if (providedEmptyOption) {
660-
selectElement.prepend(selectCtrl.emptyOption);
652+
653+
if (selectCtrl.unknownOption.parent().length) {
654+
selectCtrl.unknownOption.after(selectCtrl.emptyOption);
655+
} else {
656+
selectElement.prepend(selectCtrl.emptyOption);
657+
}
661658
}
662659

663660
options.items.forEach(function addOption(option) {
@@ -704,7 +701,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
704701
ngModelCtrl.$render();
705702
}
706703
}
707-
708704
}
709705
}
710706

src/ng/directive/select.js

+19-14
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ var SelectController =
4444
// to create it in <select> and IE barfs otherwise.
4545
self.unknownOption = jqLite(window.document.createElement('option'));
4646

47-
// The empty option is an option with the value '' that te application developer can
48-
// provide inside the select. When the model changes to a value that doesn't match an option,
49-
// it is selected - so if an empty option is provided, no unknown option is generated.
50-
// However, the empty option is not removed when the model matches an option. It is always selectable
51-
// and indicates that a "null" selection has been made.
47+
// The empty option is an option with the value '' that the application developer can
48+
// provide inside the select. It is always selectable and indicates that a "null" selection has
49+
// been made by the user.
50+
// If the select has an empty option, and the model of the select is set to "undefined" or "null",
51+
// the empty option is selected.
52+
// If the model is set to a different unmatched value, the unknown option is rendered and
53+
// selected, i.e both are present, because a "null" selection and an unknown value are different.
5254
self.hasEmptyOption = false;
5355
self.emptyOption = undefined;
5456

@@ -84,7 +86,7 @@ var SelectController =
8486

8587
self.unselectEmptyOption = function() {
8688
if (self.hasEmptyOption) {
87-
self.emptyOption.removeAttr('selected');
89+
setOptionSelectedStatus(self.emptyOption, false);
8890
}
8991
};
9092

@@ -126,14 +128,7 @@ var SelectController =
126128
var selectedOption = $element[0].options[$element[0].selectedIndex];
127129
setOptionSelectedStatus(jqLite(selectedOption), true);
128130
} else {
129-
if (value == null && self.emptyOption) {
130-
self.removeUnknownOption();
131-
self.selectEmptyOption();
132-
} else if (self.unknownOption.parent().length) {
133-
self.updateUnknownOption(value);
134-
} else {
135-
self.renderUnknownOption(value);
136-
}
131+
self.selectUnknownOrEmptyOption(value);
137132
}
138133
};
139134

@@ -176,6 +171,16 @@ var SelectController =
176171
return !!optionsMap.get(value);
177172
};
178173

174+
self.selectUnknownOrEmptyOption = function(value) {
175+
if (value == null && self.emptyOption) {
176+
self.removeUnknownOption();
177+
self.selectEmptyOption();
178+
} else if (self.unknownOption.parent().length) {
179+
self.updateUnknownOption(value);
180+
} else {
181+
self.renderUnknownOption(value);
182+
}
183+
};
179184

180185
var renderScheduled = false;
181186
function scheduleRender() {

src/ngMock/angular-mocks.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
13781378
}
13791379
}
13801380

1381+
handleResponse.description = method + ' ' + url;
13811382
return handleResponse;
13821383

13831384
function handleResponse() {
@@ -1884,7 +1885,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
18841885
$httpBackend.verifyNoOutstandingRequest = function(digest) {
18851886
if (digest !== false) $rootScope.$digest();
18861887
if (responses.length) {
1887-
throw new Error('Unflushed requests: ' + responses.length);
1888+
var unflushedDescriptions = responses.map(function(res) { return res.description; });
1889+
throw new Error('Unflushed requests: ' + responses.length + '\n ' +
1890+
unflushedDescriptions.join('\n '));
18881891
}
18891892
};
18901893

test/helpers/matchers.js

+29-4
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,15 @@ beforeEach(function() {
365365
return {
366366
compare: function(actual) {
367367
var errors = [];
368+
var optionVal = toJson(actual.value);
369+
368370
if (actual.selected === null || typeof actual.selected === 'undefined' || actual.selected === false) {
369-
errors.push('Expected option property "selected" to be truthy');
371+
errors.push('Expected option with value ' + optionVal + ' to have property "selected" set to truthy');
370372
}
371373

372374
// Support: IE 9 only
373375
if (msie !== 9 && actual.hasAttribute('selected') === false) {
374-
errors.push('Expected option to have attribute "selected"');
376+
errors.push('Expected option with value ' + optionVal + ' to have attribute "selected"');
375377
}
376378

377379
var result = {
@@ -383,13 +385,15 @@ beforeEach(function() {
383385
},
384386
negativeCompare: function(actual) {
385387
var errors = [];
388+
var optionVal = toJson(actual.value);
389+
386390
if (actual.selected) {
387-
errors.push('Expected option property "selected" to be falsy');
391+
errors.push('Expected option with value ' + optionVal + ' property "selected" to be falsy');
388392
}
389393

390394
// Support: IE 9 only
391395
if (msie !== 9 && actual.hasAttribute('selected')) {
392-
errors.push('Expected option not to have attribute "selected"');
396+
errors.push('Expected option with value ' + optionVal + ' not to have attribute "selected"');
393397
}
394398

395399
var result = {
@@ -400,6 +404,27 @@ beforeEach(function() {
400404
return result;
401405
}
402406
};
407+
},
408+
toEqualSelect: function() {
409+
return {
410+
compare: function(actual, expected) {
411+
var actualValues = [],
412+
expectedValues = [].slice.call(arguments, 1);
413+
414+
forEach(actual.find('option'), function(option) {
415+
actualValues.push(option.selected ? [option.value] : option.value);
416+
});
417+
418+
var message = function() {
419+
return 'Expected ' + toJson(actualValues) + ' to equal ' + toJson(expectedValues) + '.';
420+
};
421+
422+
return {
423+
pass: equals(expectedValues, actualValues),
424+
message: message
425+
};
426+
}
427+
};
403428
}
404429
});
405430
});

0 commit comments

Comments
 (0)