AngularJS 1.4: Select List Value not Initializing Correctly when List is Inserted with $compile
Asked Answered
C

4

15

Here's some quick background info. I just upgraded to Angular 1.4. I'm using an API written in C# for my server-side calls.

A section of my page shows 2 select lists (Project & Sub-project). Both are supposed to default to "(Select a ______)", which I list as the first option of each select with "value" of 0. The appropriate ng-model variables are initialized to 0.

The actual HTML code for the select lists is being generated on the server side using string concatenation, passed to the client via $http, and inserted using a directive that calls $compile (not ideal at all, but my client has pretty much chained me to this API). Prior to the 1.4 update, everything was working nicely.

Now, my Project select list is defaulting to nothing. When I inspect the element, this is what I see...

<select ng-change="updateSubProjList()" ng-model="curProjID">
    <option value="? number:0 ?"></option>
    <option value="0">(Select a Project)</option>
    <option value="1">First Project</option>
    <option value="2">Second Project</option>
    ...
</select>

...with that first "? number:0 ?" entry being the one that is currently selected. My Sub-project select list still initializes just fine, which makes this even more odd.

I know that there were some updates to $compile in the update to AngularJS 1.4, but I can't figure out a solution to my problem. Any assistance would be greatly appreciated.

Cataldo answered 1/6, 2015 at 20:42 Comment(2)
what is the value of curProjID?Berlyn
It's initialized to 0 when the controller loads.Cataldo
B
24

There seems to be a change in 1.4 related to how the selected option is matched against ngModel by comparing the value attribute in <option value="0"> - it now requires to explicitly use a string to match, rather than allowing for an integer.

In fact, the documentation clearly states:

The value of a select directive used without ngOptions is always a string. When the model needs to be bound to a non-string value, you must either explicitly convert it using a directive ... or use ngOptions to specify the set of options.

To fix, change the initialized value of $scope.curProjID to be a string:


$scope.curProjID = "0"; // instead of $scope.curProjID = 0;

When there is no match (and there isn't, unless you assign a string "0"), select adds an "unknown" option: <option value="? number:0 ?"></option>.

Berlyn answered 2/6, 2015 at 19:56 Comment(4)
This is absolutely right. Thank you! For anyone wondering, my 2nd select statement worked because the options came from and array of objects loaded using "ng-options", as opposed to a giant concatenated string.Cataldo
As a quick fix I just changed my ng-model binding to include the "toString()"<select ng-model="reason.toString()">Brain
@JeremyChild, that is rather a "quick break"... it might work for matching, but it would not update the model ($scope.reason) when the selection changes, which is the whole purpose of ng-modelBerlyn
Well done, Angular! My face after reading this answer was like :O.Annulet
F
5

There is another way: using a directive implementing a parser and a formatter. See http://plnkr.co/edit/JZPay8jgm5AXKGXDCjSb

thankx goes to narretz!

Pseudo code:

<select convert-number ng-model="x">
   <option value="100">100</option>
   <option value="200">200</option>
</select>


app.directive('convertNumber', function() {
  return {
    require: 'ngModel',
    link: function(scope, el, attr, ctrl) {
      ctrl.$parsers.push(function(value) {
        return parseInt(value, 10);
      });

      ctrl.$formatters.push(function(value) {
        return value.toString();
      });      
    }
  }
});
Fredrika answered 9/6, 2015 at 12:4 Comment(0)
F
2

I had the same issue. Instead of making sure that my JavaScript code uses string all over the place, I did the following:

Removed the <option ...> items from the HTML file

Introduced a list into JavaScript, similar to:

var orderQuantities = [
        { id: 100, text: '100' },
        { id: 200, text: '200' },
        { id: 300, text: '300' },
];

Did make sure that orderQuantities is visible in the scope

Used ng-option in the select tag as follow:

<select ng-model="vm.entity.OrderQuantity" ng-options="orderQuantity.id as orderQuantity.text for orderQuantity in vm.orderQuantities"></select>
Fredrika answered 8/6, 2015 at 16:49 Comment(1)
With this approach, what get bound inside vm.entity.OrderQuantity ? Was it only the id (that is, 100, 200, 300) or was it entire selected object (e.g. { id: 200, text: '200' }) ?Nihility
K
0
<select ng-model="x">
   <option value="100">100</option>
   <option value="200">200</option>
</select>

scope.x = '200'

or read this doc. https://docs.angularjs.org/guide/migration

Kessler answered 28/7, 2016 at 14:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.