-
Notifications
You must be signed in to change notification settings - Fork 27.4k
f[eat|ix](select): support values added by ngValue #13962
Conversation
3f0d15c
to
7ae0a5b
Compare
scope.selected = 'UPDATEDVALUE'; | ||
scope.$digest(); | ||
|
||
// expect(element.find('option').eq(0).prop('selected')).toBe(true); not selected in Chrome? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to fix this, but I'm not sure it's necessary. I don't think browsers have to set selected when the value of the select changes. It's strange though that is set before changing the select value via model update.
This test fails: it('should work update the value as a result of changes to the options', function() {
var A = { name: 'A'}, B = { name: 'B'}, C = { name: 'C'};
scope.options = [A, B, C];
scope.obj = {};
compile(
'<select ng-model="obj.value">' +
'<option ng-repeat="option in options" ng-value="option">{{$index}}</option>' +
'</select>'
);
var optionElements = element.find('option');
expect(optionElements.length).toEqual(4);
browserTrigger(optionElements.eq(0));
optionElements = element.find('option');
expect(optionElements.length).toEqual(3);
expect(scope.obj.value).toBe(A);
scope.options.shift();
scope.$digest();
optionElements = element.find('option');
expect(optionElements.length).toEqual(3);
expect(scope.obj.value).toBe(null);
}); |
Have you tried if that works with regular interpolated options? Because I don't think it does |
It certainly works with |
518a8a6
to
7270826
Compare
@petebacondarwin I've updated the PR again with the changes we talked about (support for disabled options, and model updates when the options change / are removed / get disabled / get added). |
handleMultipleChanges = false; | ||
self.ngModelCtrl.$setViewValue(self.readValue()); | ||
if (renderAfter) self.ngModelCtrl.$render(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern of running something once in the postDigest keeps appearing. I wonder if we should abstract it out into $scope?
I think you might have nailed it but it is the kind of change that I want to spend some time thinking about corner cases... |
abafb21
to
7bdc6b8
Compare
738af0b
to
06e905e
Compare
We found a Contributor License Agreement for you (the sender of this pull request) and all commit authors, but as best as we can tell these commits were authored by someone else. If that's the case, please add them to this pull request and have them confirm that they're okay with these commits being contributed to Google. If we're mistaken and you did author these commits, just reply here to confirm. |
1 similar comment
We found a Contributor License Agreement for you (the sender of this pull request) and all commit authors, but as best as we can tell these commits were authored by someone else. If that's the case, please add them to this pull request and have them confirm that they're okay with these commits being contributed to Google. If we're mistaken and you did author these commits, just reply here to confirm. |
I did author some of these commits and I am OK with that :-) |
@@ -14,10 +14,15 @@ var SelectController = | |||
['$element', '$scope', function($element, $scope) { | |||
|
|||
var self = this, | |||
optionsMap = new HashMap(); | |||
optionsMap = new HashMap(), | |||
handleMultipleDestroy = false; // Flag to run an update to the model after selected options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no longer needed?
I'll change the tests and then add some docs. Should I mentiond that performance in IE is going to be very bad with 3000+ options? |
Why not |
While fixing up the PR, I found a behavior in ngOptions and select that I find a bit confusing: You have a select that is multiple and the model contains two values, one of which is matched by an option, while the other isn't. |
So in #13172 we decided that if no option at all can be selected, then the select should be invalid (required). Doesn't say anything about a partial mismatch, though. We could decide not to select anything when not all options are matched, but I'm not convinced that this is a good idea. Theoretically, setting the model to match the available options is the developers responsibility, so if that happens it's your own fault. However, it's not very easy to check if a select's options will match the model. We could expose the |
I'm also fine with making a decision on this later. As far as I can tell, the described behavior hasn't bothered anyone yet. |
128f6e8
to
91cafff
Compare
select elements with ngModel will now set ngModel to option values added by ngValue. This allows setting values of any type (not only strings) without the use of ngOptions. Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set, which does not have any type restriction. Any $observe on the value attribute will therefore receive the original value (result of ngValue expression). However, when a user selects an option, the browser sets the select value to the actual option's value attribute, which is still always a string. For that reason, when options are added by ngValue, we set the hashed value of the original value in the value attribute and store the actual value in an extra map. When the select value changes, we read access the actual value via the hashed select value. Since we only use a hashed value for ngValue, we will have extra checks for the hashed values: - when options are read, for both single and multiple select - when options are written, for multiple select I don't expect this to have a performance impact, but it should be kept in mind. Closes angular#9842 Closes angular#6297 BREAKING CHANGE: `<option>` elements added to `<select ng-model>` via `ngValue` now add their values in hash form, i.e. `<option ng-value="myString">` becomes `<option ng-value="myString" value="string:myString">`. This is done to support binding options with values of any type to selects. This should rarely affect applications, as the values of options are usually not relevant to the application logic, but it's possible that option values are checked in tests.
These rules follow ngOptions behavior: - when an option that is currently selected, is removed or its value changes, the model is set to null. - when an an option is added or its value changes to match the currently selected model, this option is selected. - when an option is disabled, the model is set to null. - when the model value changes to a value that matches a disabled option, this option is selected (analogue to ngOptions)
91cafff
to
7b50f49
Compare
✨ 🎉 ✨ |
select elements with ngModel will now set ngModel to option values added by ngValue.
This allows setting values of any type without the use of ngOptions.
Interpolations inside attributes can only be strings, but the ngValue directive uses attrs.$set,
which does not follow any type restriction. Any $observe on the value attribute will therefore receive
the original value (result of ngValue expression). However, when a user selects an option, the browser
sets the select value to the actual option's value attribute, which is still always a string.
For that reason, when option are added by ngValue, we set the hashed value of the original value in
the value attribute and store the actual value in an extra map. When the select value changes, we
read access the actual value via the hashed select value.
Closes #9842
Closes #6297
Talking points:
hackyspecialafaik angular 2 suffers from the same problem as ng1 right now