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

SELECTs ng-model values not being bound for non-strings #13955

Closed
campbeln opened this issue Feb 5, 2016 · 6 comments
Closed

SELECTs ng-model values not being bound for non-strings #13955

campbeln opened this issue Feb 5, 2016 · 6 comments

Comments

@campbeln
Copy link

campbeln commented Feb 5, 2016

We have an issue with new === ng-options/select's ng-models value comparisons. A change with 1.4+ was to always have:

<option value="2">Two</option>

be === "2" as all underlying values are strings (fair enough, but isn't this true of all HTML values?) rather than == 2 as per <1.4. The problem is none of our non-string bound selects are binding anymore because 2 !== "2".

From == to === is a good change, but it seems the Angular team hasn't bothered to coerce the left-hand side of the expression into a string but has left that on us as devs to do it?! Really? So I have to change _ALL_ of my APIs rather than the Angular team adding value + '' on the left?

I'm working on a major Australian government system that uses Angular and we are unable to upgrade past 1.3 currently because of this problem. Or have we managed to miss an easy fix for this issue?

Here's an extremely simplified example of the issue we're experiencing:

<script>
    $scope.data = { age: 2 };
<script>

<select ng-model="data.age">
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
</select>

Angular 1.3 binds the select without issue, while 1.4+ does 2 === "2" et'al and of course doesn't find a match and leaves the select as blank.

Our experience with replacing the HTML options with ng-options has not fixed the issue (nor would it be appropriate in our case to deploy system wide). This is likely due to having both a select and track by in the ng-options expression (which are gawd awful, by the way) which we have only recently found are non-compatible.

Is the Angular team really saying that from 1.4+ we are no longer able to bind to a static options list if our underlying value is non-string? Are these changes likely to be rolled out to <input type="radio"/>s and <input type="checkbox"/>s as well?

@campbeln campbeln changed the title ng-options/SELECTs ng-models value comparisons SELECTs ng-model values not being bound for non-strings Feb 5, 2016
@Narretz
Copy link
Contributor

Narretz commented Feb 6, 2016

Hi, this issue has been documented pretty extensively and is marked as a breaking change: https://code.angularjs.org/snapshot/docs/guide/migration#select with suggested workarounds.

The change was necessary, because otherwise scope.model = true would match value="1" and that is simply wrong. The matching should work in a predictable way, and that is more important than the convenience of converting number strings to actual strings. It's also consistent, as all attribute values are always strings.
We also do not want to convert the model to a string value (I assume that's what you mean by coercing the "left-hand side of the expression into a string"), because then we would overwrite the model with a different type, which is also unexpected and can cause confusion / errors.
Lastly, the restriction that all option values have to be strings is very old - that's why we have the ngOptions directive. So this worked merely by accident for you and was a bug, not a feature.

@Narretz Narretz added this to the Purgatory milestone Feb 6, 2016
@campbeln
Copy link
Author

campbeln commented Feb 6, 2016

But if you stringify the left-hand side of the comparison, you'd get "true" === "1" (or "undefined" === "false" or "" === "false" or "null" === "false") which are not equal. Truthy/falsy wouldn't cause its problems any longer.

And it's odd to have inconsistent handling of SELECTs from INPUT[type=radio] and INPUT[type=checkbox], or are these now similarly broken? There is no ng-optionsesque native directive for iterating over these, nor should there be considering their HTML/layout/etc.

@campbeln
Copy link
Author

campbeln commented Feb 6, 2016

The only potentional issue would be "[Object]" === "[Object]" but then how did the Object get string'ed into the OPTION in the first place?

@Narretz
Copy link
Contributor

Narretz commented Feb 8, 2016

It would get stringified if somebody tried to interpolate an object. If we are allowing numbers, boolean etc. by being clever with the comparison, there will be definitely be someone who tries the same with an object or an array, and that will fail. Simply put, interpolation produces strings, and attributes can only hold strings, so any way we are trying to be clever will break this contract and leat to broken edge cases. I have an idea how to use ngValue for binding arbitrary values to a select: #13962 But even this suffers from the problem that we have to work around the restriction for string values.

@gkalpak
Copy link
Member

gkalpak commented Feb 8, 2016

Another potential issue is when someone does not want to have a $modelValue of 1 select an element with value "1". As mentioned in the relevan sections of the changelog and the migration guide, you can do the toString() comparison yourself (either globally or targeting specific selects).

See a demo of this here.

@Narretz
Copy link
Contributor

Narretz commented Feb 17, 2016

I'm gonna close this, as there is currently nothing we can do about this / this works as expected.

@Narretz Narretz closed this as completed Feb 17, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants