ng-options is only working for name, not value - angularjs

I've seen a few questions here similar to this. In fact that's how I got this far. But one little piece isn't working. I have the following:
<select id="facility" class="form-control"
ng-model="MainObj.facility" name="facility"
ng-options="fclty.value as fclty.name for fclty in MainData.fclts">
<option value=""></option>
</select>
I've checked via logging that MainData.fclts contains exactly what I want, but each option outputs as
<option value="0" label="Actual Facility Name">Actual Facility Name</option>
<option value="1" label="Actual Facility Name">Actual Facility Name</option>
(etc)
In other words, it's putting the name in there correctly, but just using a 0, 1, 2, ... for the value instead of what's in the value field.
What am I missing here?
Thanks.
EDIT:
Sample data:
[value: '00001', name: "John's House"]
[value: '00002', name: "School"]
[value: 'testval', name: "Testing Building"]
(etc)
EDIT 2:
Just for the fun of it, I swapped the two and did fclty.name as fclty.value. Sure enough the value showed up in the dropdown but the value portion of the option was still the default 0, 1, 2, ..
So, it can SEE that field if it wants to, but, for some reason, doesn't want to use it to fill in the value of the option.

What does your MainObj and MainData look like? This example works exactly as it should:
angular.module("myApp",[])
.controller("main",["$scope", function($scope){
$scope.MainData = {
fclts: [
{name:"Place", value: "00001"},
{name:"Other Place", value: "00002"},
{name:"Thrid Place", value: "testval"}
]
};
$scope.MainObj = {facility: "00002"};
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<body ng-app="myApp" ng-controller="main">
<select ng-model="MainObj.facility" name="facility" ng-options="fclty.value as fclty.name for fclty in MainData.fclts">
</select>
{{MainObj.facility}}
</body>
Your sample data is not in valid syntax, please confirm that they are formatted like objects:
[
{value: "00001", name: "John's House"},
{value: "00002", name: "School"},
{name:"Thrid Place", value: "testval"}
]

With AngularJS V1.3, the ng-options directive sets the value to the $index of the option.
<option value="" class=""></option>
<option value="0" label="John's House">John's House</option>
<option value="1" label="School">School</option>
<option value="2" label="Testing Building">Testing Building</option>
With AngularJS V1.4, the ng-options directive sets the value differently:
<option value="" class=""></option>
<option label="John's House" value="string:00001">John's House</option>
<option label="School" value="string:00002">School</option>
<option label="Testing Building" value="string:testval">Testing Building</option>
Between V1.3 and V1.4, the ng-options directive underwent major refactoring.
From the Docs:
ngOptions
The ngOptions directive has also been refactored and as a result some long-standing bugs have been fixed. The breaking changes are comparatively minor and should not affect most applications.
Due to 7fda214c, when ngOptions renders the option values within the DOM, the resulting HTML code is different. Normally this should not affect your application at all, however, if your code relies on inspecting the value property of <option> elements (that ngOptions generates) then be sure to read the details.
Due to 7fda214c, when iterating over an object's properties using the (key, value) in obj syntax the order of the elements used to be sorted alphabetically. This was an artificial attempt to create a deterministic ordering since browsers don't guarantee the order. But in practice this is not what people want and so this change iterates over properties in the order they are returned by Object.keys(obj), which is almost always the order in which the properties were defined.
Also due to 7fda214c, setting the ngOptions attribute expression after the element is compiled, will no longer trigger the ngOptions behavior. This worked previously because the ngOptions logic was part of the <select> directive, while it is now implemented in the ngOptions directive itself.
— AngularJS Developer Guide - Migrating from V1.3 to V1.4 - ngOptions

Related

Angular Select List Binding an Object when I was expecting a Value

I wonder if I am going about this all wrong, but here's the issue.
I have a Select List configured like so:
<select name="{{ measure.Name }}_MeasureId"
ng-model="CompleteDataSetViewModel.AnnualRecord.TargetHazardousWaste_Original_MeasureId"
ng-options="obj.Text for obj in measure.AnnualTarget_Measure_SelectList track by obj.Value"
ng-init="CompleteDataSetViewModel.AnnualRecord.TargetHazardousWaste_Original_MeasureId = measure.AnnualTarget_Measure_SelectList[measure.InitialFigure_Measure - 1]"
class="" required></select>
This outputs what I want:
<option selected="selected" value="?"></option>
<option value="13">Metric Tonnes </option>
<option value="14">Imperial Tonnes</option>
<option value="15">Short Tonnes </option>
What I then want in my JSON is the value of CompleteDataSetViewModel.AnnualRecord.TargetHazardousWaste_Original_MeasureId to be set to a numeric value of let's say 13. However, what I actually get is an object like this:
"TargetHazardousWaste_Original_MeasureId": {
"Text": "Metric Tonnes ",
"Value": "13",
"Selected": false
},
This is somewhat correct - the right item is picked, but what I am after is simply the 13 as that is the value I want to write to my database.
I have considered doing some kind of mapping exercise to effectively extract the value and assign it, but that feels counter to what Angular appears to be about (I've only been at it 2 days).
I've tried the Angular documentation, but that's left me feeling more confused. Is there something I am missing?
Change your ng-options to this:
obj.Value as obj.Text for obj in measure.AnnualTarget_Measure_SelectList track by obj.Value
Without that, you're making it so the one they select is the entire object.

Initial ng-model value not set in select

I have an enum (I code using TypeScript):
export enum AddressType
{
NotSet = 0,
Home = 1,
Work = 2,
Headquarters = 3,
Custom = -1,
}
Then in my controller I have a field named type, into which I set the initial value that should be selected in the select input (I set it to AddressType.Headquarters).
Finally, in my HTML I put the following:
<select ng-model="Ctrl.type" ng-options="addressType for addressType in Ctrl.getAddressTypes()"></select>
Everything seems to work fine except one thing: for some reason Angular does not select "3" (Headquarters) initially in the select after all bindings have been updated. Angular creates an extra option like this instead:
<option value="?" selected="selected"></option>
So for some reason Angular cannot figure the initial option to select in the combo.
If the user selects another option of the combo box, Ctrl.type is updated properly so the binding works fine for that part. Basically my problem is just that the option that should be selected initially is not selected as expected.
What am I missing here that is causing that problem?
Found the problem:
The array returned by Ctrl.getAddressTypes() was an array of strings:
["0", "1", "2", "3", "1"]
and what was stored in Ctrl.type was of type number.
AngularJS compares the array supplied to ng-options to the value supplied to ng-model using the '===' operator. 3 does not equal to "3" in that case - that's why it did not work.
I often run into this when using number id's. My way around this quirk is to add ''+ to convert it to string type:
<select ng-options="''+u.id as u.name for u in users"
In a function if the below code is added and the same is called from the ng-init then the issue is also getting resolved. This will resolve the string comparison issue.
$scope.Ctrl.type = "" + $scope.Ctrl.type + "";
I happens because you didn't initiated selected value. Try to set init value with ng-init:
<select ng-model="Ctrl.type"
ng-options="addressType for addressType in Ctrl.getAddressTypes()"
ng-init="Ctrl.type = ..."
></select>
See this Demo Fiddle where we have 2 combos with and without init value. You can see that one combo HTML seems like:
<select ng-model="selectedItem1"
ng-options="selectedItem1.name as selectedItem1.name for selectedItem1 in values" class="ng-pristine ng-valid">
<option value="?" selected="selected"></option>
<option value="0">General</option>
<option value="1">Super</option>
<option value="2">Trial</option>
</select>
The proper one:
<select ng-model="selectedItem"
ng-options="selectedItem.name as selectedItem.name for selectedItem in values"
ng-init="selectedItem = values[1].name" class="ng-pristine ng-valid">
<option value="0">General</option>
<option value="1" selected="selected">Super</option>
<option value="2">Trial</option>
</select>

Angular adds strange options into select element when setting model value

I have a select element defined as such:
<select name="country_id" id="country_id" required="required" ng-model="newAddressForm.country_id">
<option value="">Select Country</option>
<option ng-repeat="country in countries" value="{{country.id}}">{{country.name}}</option>
</select>
All works fine when I'm not setting any kind of value in the directive which contains this select element. But when I do something like newAddressForm.country_id = 98, instead of selecting the option with value 98, Angular injects a new one at the top of the select element, like so:
<option value="? string:98 ?"></option>
What gives? What sort of format is this and why does this happen? Note that if I do a console.log(newAddressForm.country_id) in the directive, I get a normal "98", it's just weird in the generated HTML.
Edit: Situation update. Switched to using ng-select, but the issue persists.
The weird element no longer appears, BUT, now there's another element at the top, one that has only a question mark ? as the value, and no label.
That's, from what I gathered, Angular's "none selected" option. I still don't understand why it won't select the option I tell it to select, though.
Doing newAddressForm.country_id = 98 still gives no results. Why is that?
Using the following syntax with ng-options solved this problem for me:
<select name="country_id" id="country_id" required="required" ng-model="newAddressForm.country_id" ng-options="country.id as country.name for country in countries">
<option value="">Select Country</option>
</select>
Angular does not set the value of a select element to the actual values of your array and does some internal things to manage the scope binding. See Mark Rajcok's first comment at this link:
https://docs.angularjs.org/api/ng/directive/select#overview
When the the user selects one of the options, Angular uses the index
(or key) to lookup the value in array (or object) -- that looked-up
value is what the model is set to. (So, the model is not set to the
value you see in the HTML! This causes a lot of confusion.)
I'm not entirely sure using an ng-repeat is the best option.
If your values are integers you should use "" even if they're not strings, that simple reason is exactly why you're getting an option with a question mark as a value.
You shouldn't be using this:
{ value: 0, name: "Pendiente" },
{ value: 1, name: "Em andamento" },
{ value: 2, name: "Erro" },
{ value: 3, name: "Enviar email" },
{ value: 4, name: "Enviado" }
This is the right way:
{ value: "0", name: "Pendiente" },
{ value: "1", name: "Em andamento" },
{ value: "2", name: "Erro" },
{ value: "3", name: "Enviar email" },
{ value: "4", name: "Enviado" }
If you've at least one record which isn't using "" you'll be getting this ? option value.
I was running into this same problem earlier this evening, where I saw a select option showing up as the first in my list even though I didn't explicitly create it. I was filling a list of select options in my controller and using the same ng-options syntax mentioned above by jessedvrs (except that I was also inserting the "select an option" default option in the controller rather than marking it in the HTML like he was).
For some reason the select list would always show an additional option at index zero with a value of "?", but when I changed how I filled my default option in the controller, this issue went away. I was populating the select options by making an API call, filling them inside of a promise. I made the mistake of also populating my default "select an option" option as the first thing I did in that promise, but when I did this instead outside of the promise (prior to making the API call), the select options populated the way I waned them to.
I think jessedvrs option is one solution to the problem (setting the default option in the HTML markup), but if you prefer to populate your options in javascript instead, I would suggest to still set the default option prior to making any calls to an API or processes that may still be running while the HTML is being rendered.
When you assign a value to some select element, AngularJS looks for the provided value in the value attribute of the option tags in that select element. But the catch is, AngularJS does a type based comparison. So if the values in the option tags are strings (which usually is the case) and the variable you bind using ng-model is a number, AngularJS fails to find the matching option element and hence, creates its own element like this -
<option value="? integer:10 ?"></option>
The solution is, while binding itself, convert it to the appropriate type.
In this case, the solution would be to bind an Integer
<select name="country_id" id="country_id" required="required" ng-model="parseInt(newAddressForm.country_id)">
<option value="">Select Country</option>
<option ng-repeat="country in countries" value="{{country.id}}">{{country.name}}</option>
</select>
If the values are set as Strings, the trick would be to use
<select ... ng-model="newAddressForm.country_id.toString()" >
use " track by 'value' " at the end of your ng-options :D
like the example below:
ng-options="country.id as country.name for country in countries track by country.id"
While pushing data on scope variable we can use following code
$scope.enquiryLocationRow.push({'location': EnqLocation.location, 'state_id': Number(EnqLocation.state_id), 'country_id': Number(EnqLocation.country_id)});
it resolved my issue
vm.kanoonCourses is object array, vm.courses is string array :
<table class="table table-hover">
<tr style="font-weight: bold; background-color: #dfedf8;">
<td>index</td>
<td>course in our system</td>
<td>course in outside system</td>
</tr>
<tr ng-repeat="course in vm.courses">
<td style="vertical-align: middle;">{{$index+1|persianNumber}}</td>
<td style="vertical-align: middle;">{{course.CourseTitle}}</td>
<td>
<select
ng-model="course.KanoonCourseTitle"
ng-options="option as option for option in vm.kanoonCourses">
</select>
</td>
</tr>
</table>

Using $index with the AngularJS 'ng-options' directive?

Say that I bind an array to a select tag using the following:
<select ng-model="selData" ng-options="$index as d.name for d in data">
In this case, the associated option tags are assigned a sequence of index values: (0, 1, 2, ...). However, when I select something from the drop-down, the value of selData is getting bound to undefined. Should the binding actually work?
On the other hand, say that I instead do the following:
<select ng-model="selData" ng-options="d as d.name for d in data">
Here, the option tags get the same index, but the entire object is bound on change. Is it working this way by design, or this behavior simply a nice bug or side-effect of AngularJS?
Since arrays are very similar to objects in JavaScript, you can use the syntax for "object data sources". The trick is in the brackets in the ng-options part:
var choices = [
'One',
'Two',
'Three'
];
In the template:
<select
ng-model="model.choice"
ng-options="idx as choice for (idx, choice) in choices">
</select>
In the end, model.choice will have the value 0, 1, or 2. When it's 0, you will see One; 1 will display Two, etc. But in the model, you will see the index value only.
I adapted this information from "Mastering Web Application Development with AngularJS" by PACKT Publishing, and verified at the Angular reference documentation for select.
Since you can't use $index but you can try indexOf.
HTML
<div ng-app ng-controller="MyCtrl">
<select
ng-model="selectedItem"
ng-options="values.indexOf(selectedItem) as selectedItem for selectedItem in values"></select>
selectedItem: {{selectedItem}}
</div>
Controller
function MyCtrl($scope) {
$scope.values = ["Value1","Value2"];
$scope.selectedItem = 0;
}
Demo Fiddle
Comment:
Array.prototype.indexOf is not supported in IE7 (8)
$index is defined for ng-repeat, not select. I think this explains the undefined. (So, no, this shouldn't work.)
Angular supports binding on the entire object. The documentation could be worded better to indicate this, but it does hint at it: "ngOptions ... should be used instead of ngRepeat when you want the select model to be bound to a non-string value."
You can also use ng-value='$index' in <option> tag.
<select ng-model="selData">
<option ng-repeat="d in data track by $index" ng-value="$index">
{{d.name}}
</option>
</select>
Don't use $index inside select tags. Use $index inside the option tags if you want to use the array indexes as either values or options.
<option ng-repeat="user in users" value="{{user.username}}">{{$index+1}}</option>
If you want to use inside values just put it in the value attribute as binding expression like
<option ng-repeat="user in users" value="{{$index+1}}">{{user.username}}</option>
and my controller code be like:
var users = ['username':'bairavan', 'username':'testuser'];

Angularjs ngOption with array

I want to add an html select with options AM,PM with angularjs,
what i need is having the key and the value of the option be the same :
<option value="AM">AM</option>
<option value="PM">PM</option>
My html looks like this
<select ng-model="ampm" ng-options="k as v for (k , v) in ampms"></select>
and my controller looks like
$scope.ampm = (new Date().getHours()) >= 12 ? 'PM' : 'AM';
$scope.ampms ={"AM":"AM","PM":"PM"};
and every thing working fine.
My question is why i cant have the same thing when i used an array (i tried all the options in the ng-options)
as this
$scope.ampms =["AM","PM"];
what ever i do i always get this
<option value="0">AM</option>
<option value="1">PM</option>
What i want is using an array like above with the option has the key and the value the same.
With AngularJS, you don't need to worry about what the value of the option is. All the selects I've seen with ng-options have values of 0 through whatever. If you're just looking for a dropdown with the two options, it can be as simple as
<select ng-model="ampm" ng-options="currOption for currOption in ['AM', 'PM']"></select>
See http://jsfiddle.net/EyBVN/1/
This is is a default behavior of ng-options in Angular. If you do not specify a key name, angular will automatically choose to use the index rather than a key. The code that does that can be seen on line 405 in /src/ng/directives/select.js on Angular's Github repository.
It can't even be forced by "value as value for (index, value) in values".
But as dnc253 just beat me to the punch with his answer (it showed up while I was typing)... you don't have to worry about it, Angular does it all for you automatically.
I did find a way to place specific data in the value of the options for a select. You have to add an ng-repeat attribute to the option tag inside the select tag:
<select id="{{question.id}}" name="{{question.id}}"
class="{{question.inputclass}}" ng-required="question.required"
title="{{question.title}}">
<option value=""></option>
<optgroup ng-repeat="group in question.data" label="{{group.group}}">
<option ng-repeat="item in group.data" value="{{item.value}}"
ng-selected="{{item.value == question.defaultValue}}">
{{item.description}}
</option>
</optgroup>
</select>
As a bonus, I left the option group tags in place to serve as an example for everyone.
The question.data JSON is:
[
{"group":"Canada","data":[{"value":"Ontario","description":"Toronto"},
{"value":"Quebec","description":"Quebec City"},
{"value":"Manitoba","description":"Winnipeg"}
]
},
{"group":"Mexico","data":[{"value":"Jalisco","description":"Guadalajara"},
{"value":"Nayarit","description":"Tepic"}
]
},
{"group":"United States of America","data":[
{"value":"Alabama","description":"Montgomery"},
{"value":"Georgia","description":"Atlanta"},
{"value":"Mississippi","description":"Jackson"},
{"value":"Louisiana","description":"Baton Rouge"},
{"value":"Texas","description":"Ausint"}
]
}
]

Resources