ng-model is number, select box option values are strings - angularjs

I have the following code
<select ng-disabled="currentQuestion.id" ng-change="loadTopics()" class="detail-subject-select browser-default" ng-model="currentQuestion.SubjectId">
<option disabled="disabled" value="any">Choose a Subject</option>
<option value="1">K8-English</option>
<option value="2">K8-Math</option>
</select>
The issue is that value 1 and 2 are strings. I need them to be numbers. Everything works fine when I select one on my page, but I need the select box to initialize with the value of (currentQuestion.SubjectId which is a number) when the page loads.
How can I get around this?

Standard HTML attributes (like "value") always mean strings. Unfortunately, You can only achieve this with ng-options:
Support for select models with non-string values is available via
ngOptions.
https://docs.angularjs.org/api/ng/directive/ngValue
It may seem a bit dirty, but with more options You actually save some typing:
<select
ng-disabled="currentQuestion.id"
ng-change="loadTopics()"
class="detail-subject-select browser-default"
ng-model="currentQuestion.SubjectId"
ng-options="value as key for (key, value) in {
'K8-English': 1,
'K8-Math': 2
}"
>
<option
disabled="disabled"
value=""
>Choose a Subject</option>
</select>

One way is to define your options as an array of objects in your controller like this:
$scope.options = [{
value: 1,
name: 'K8-English'
}, {
value: 2,
name: 'K8-Math'
}];
And implement this in your HTML using ng-options like this:
<select ng-disabled="currentQuestion.id"
ng-change="loadTopics()"
class="detail-subject-select browser-default"
ng-model="currentQuestion.SubjectId"
ng-options="option.value as option.name for option in options">
<option disabled="disabled" value="any">Choose a Subject</option>
</select>

Related

Angular ng-options broken 1.4+

Angular < 1.4 works with ng-options shown as such:
<select ng-options="option.value as option.label for option in options" ng-model="selectedValue">
With the following array of options:
[{ value:"4_220588",label:"dropdown 1-test value 1"},{value:"4_220589",label:"dropdown 1-test value 2"}]
If you look at the resultant HTML is is as you would expect:
<select ng-options="option.value as option.label for option in options" ng-model="selectedValue" class="ng-pristine ng-valid ng-touched">
<option value="" class="">-- SELECT ONE --</option>
<option value="0" label="dropdown 1-test value 1">dropdown 1-test value 1</option>
<option value="1" label="dropdown 1-test value 2">dropdown 1-test value 2</option>
</select>
As soon as you change the angular version to Angular 1.4+, the option value attribute get's messed up. Here is the output with the same ng-options using a newer version of angular:
<select ng-options="option.value as option.label for option in options" ng-model="selectedValue" class="ng-pristine ng-valid ng-empty ng-touched">
<option value="" class="" selected="selected">-- SELECT ONE --</option>
<option label="dropdown 1-test value 1" value="string:4_220588">dropdown 1-test value 1</option>
<option label="dropdown 1-test value 2" value="string:4_220589">dropdown 1-test value 2</option>
</select>
What is the solution to getting the value to show up still as the index of the array?
Here is the plnkr: http://plnkr.co/edit/3CTUI9b9ntTGWhXDNQI5?p=preview
Your application logic should not be sensitive to the value attribute in the dropdown, because ng-model will set the model correctly regardless of what is output in the HTML. If you application logic does expect a specific format for this attribute, you have 3 ways to deal with this breaking change.
Use ng-repeat instead of ng-options. This is the least recommended option, as it changes the way the select lists work significantly.
Use a track by clause to enforce the key format that you are expecting, i.e. option.value as option.label for option in options track by option.value. This presumes that option.value exists and is the value you wish to represent. http://plnkr.co/edit/TSXfkpf1lhsE9QYa2NAc?p=preview
Change your application logic to expect the hashkey instead, or preferably correct the logic so that it only relies upon ng-model.
One solution would be to use ng-repeat over the options.
<select ng-model="vm.selectedValue">
<option value="" selected disabled>-- SELECT ONE --</option>
<option ng-repeat="option in options"
value="$index"
ng-selected="option === vm.selectedValue">
{{option.label}}
</option>
</select>
Here is your updated Plunkr.
This should do the trick:
<select ng-options="index as option.label for (index, option) in options2" ng-model="vm.selectedValue">
<option value="">-- SELECT ONE --</option>
</select>
On render the value of the selected option would be string:index but will output the desired value without the type included. Here is your edited plunker with the expected result in the Selected Value:
Plunker

Angular expression for ngoptions on concatenated array

I have an array from the backend that contains an array of objects that are options for a select box, let's say for countryselection:
$scope.countries = [
{ name: 'foo', code: 'ba' },
{ name: 'bar', code: 'br' },
{ name: 'biz', code: 'bb' }
];
This array is raw and reusable, and may update at any time through different functions that may change this array.
However, I want the selectbox to be prepended with a different option that is initially selected and implementation specific (so will not add this to the countries array to not pollute it for other consumers of the values):
$scope.defaultCountry = [
{ name: 'Select a country', code: '' }
];
Then I want this to work:
<select name="countryCode" model="model.country"
ng-options="option.code as option.name for option in defaultCountry + countries">
</select>
where the defaultCountry + countries would be a concat of the 2 arrays. However, this concat does not work.
I know I can call a function to return the concatenated value, but if not required I would rather not:
$scope.getCountries = function () {
return $scope.defaultCountry.concat($scope.countries);
};
<select name="countryCode" model="model.country"
ng-options="option.code as option.name for option in getCountries()">
</select>
Is there an out of the box solution to be able to use both 2 arrays at once for ng-options?
Use concat in ng-options
<select name="countryCode" ng-model="model.country"
ng-options="option.code as option.name for option in defaultCountry.concat(countries)">
</select>
This should do :
<select name="countryCode" model="model.country"
ng-options="option.code as option.name for option in countries">
<option value="">-- Select a country --</option>
</select>
You can concat before ng-option :
<select name="countryCode" model="model.country" ng-init="allCountries=defaultCountry.concat(countries)"
ng-options="option.code as option.name for option in allCountries">
<option value="">-- Select a country --</option>
</select>

How can I make AngularJS 1.2 rc store the actual values in a <select>?

I have the following object:
[
{"id":"150c67d4-952b-45b0-b287-f651a5f6d82b","name":"xx"},
{"id":"9001f011-3a0c-4d45-a0fb-eabb4c83ff83","name":"yy"},
{"id":"9b8b93af-cfef-451a-8dda-7373d9154f60","name":"zz"}
]
Here's my HTML:
<select
data-ng-model="option.selectedCreatedBy"
data-ng-options="item.id as item.name for item in option.userProfilesPlus">
<option style="display: none" value="">Select User</option>
</select>
The result is:
<select
data-ng-model="option.selectedCreatedBy"
data-ng-options="item.id as item.name for item in option.userProfilesPlus"
><option style="display: none" value="" class="">Select User</option>
<option value="0" selected="selected">*</option>
<option value="1">xx</option>
<option value="2">yy</option>
<option value="3">zz</option></select>
How can I make this so that the values stored are the actual id's ?
When you are using the ng-option to generate the option list, you don't have control over the values generated for options. For an array it is the index into the array.
If you want to have specific value in the option field, you have to use ng-repeat for it.
In any case, you should not be very concern about specific of html generated, angular can update model correctly in any case.

How to set the value attribute for select options?

Source JSON data is:
[
{"name":"Alabama","code":"AL"},
{"name":"Alaska","code":"AK"},
{"name":"American Samoa","code":"AS"},
...
]
I try
ng-options="i.code as i.name for i in regions"
but am getting:
<option value="?" selected="selected"></option>
<option value="0">Alabama</option>
<option value="1">Alaska</option>
<option value="2">American Samoa</option>
while I am expecting to get:
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AS">American Samoa</option>
So, how to get value attributes and get rid of "?" item?
By the way, if I set the $scope.regions to a static JSON instead of AJAX request's result, the empty item disappears.
What you first tried should work, but the HTML is not what we would expect. I added an option to handle the initial "no item selected" case:
<select ng-options="region.code as region.name for region in regions" ng-model="region">
<option style="display:none" value="">select a region</option>
</select>
<br>selected: {{region}}
The above generates this HTML:
<select ng-options="..." ng-model="region" class="...">
<option style="display:none" value class>select a region</option>
<option value="0">Alabama</option>
<option value="1">Alaska</option>
<option value="2">American Samoa</option>
</select>
Fiddle
Even though Angular uses numeric integers for the value, the model (i.e., $scope.region) will be set to AL, AK, or AS, as desired. (The numeric value is used by Angular to lookup the correct array entry when an option is selected from the list.)
This may be confusing when first learning how Angular implements its "select" directive.
You can't really do this unless you build them yourself in an ng-repeat.
<select ng-model="foo">
<option ng-repeat="item in items" value="{{item.code}}">{{item.name}}</option>
</select>
BUT... it's probably not worth it. It's better to leave it function as designed and let Angular handle the inner workings. Angular uses the index this way so you can actually use an entire object as a value. So you can use a drop down binding to select a whole value rather than just a string, which is pretty awesome:
<select ng-model="foo" ng-options="item as item.name for item in items"></select>
{{foo | json}}
If you use the track by option, the value attribute is correctly written, e.g.:
<div ng-init="a = [{label: 'one', value: 15}, {label: 'two', value: 20}]">
<select ng-model="foo" ng-options="x for x in a track by x.value"/>
</div>
produces:
<select>
<option value="" selected="selected"></option>
<option value="15">one</option>
<option value="20">two</option>
</select>
If the model specified for the drop down does not exist then angular will generate an empty options element. So you will have to explicitly specify the model on the select like this:
<select ng-model="regions[index]" ng-options="....">
Refer to the following as it has been answered before:
Why does AngularJS include an empty option in select? and this fiddle
Update: Try this instead:
<select ng-model="regions[index].code" ng-options="i.code as i.name for i in regions">
</select>
or
<select ng-model="regions[2]" ng-options="r.name for r in regions">
</select>
Note that there is no empty options element in the select.
You could modify you model to look like this:
$scope.options = {
"AL" : "Alabama",
"AK" : "Alaska",
"AS" : "American Samoa"
};
Then use
<select ng-options="k as v for (k,v) in options"></select>
It appears it's not possible to actually use the "value" of a select in any meaningful way as a normal HTML form element and also hook it up to Angular in the approved way with ng-options. As a compromise, I ended up having to put a hidden input alongside my select and have it track the same model as my select, like this (all very much simplified from real production code for brevity):
HTML:
<select ng-model="profile" ng-options="o.id as o.name for o in profiles" name="something_i_dont_care_about">
</select>
<input name="profile_id" type="text" style="margin-left:-10000px;" ng-model="profile"/>
Javascript:
App.controller('ConnectCtrl',function ConnectCtrl($scope) {
$scope.profiles = [{id:'xyz', name:'a profile'},{id:'abc', name:'another profile'}];
$scope.profile = -1;
}
Then, in my server-side code I just looked for params[:profile_id] (this happened to be a Rails app, but the same principle applies anywhere). Because the hidden input tracks the same model as the select, they stay in sync automagically (no additional javascript necessary). This is the cool part of Angular. It almost makes up for what it does to the value attribute as a side effect.
Interestingly, I found this technique only worked with input tags that were not hidden (which is why I had to use the margin-left:-10000px; trick to move the input off the page). These two variations did not work:
<input name="profile_id" type="text" style="display:none;" ng-model="profile"/>
and
<input name="profile_id" type="hidden" ng-model="profile"/>
I feel like that must mean I'm missing something. It seems too weird for it to be a problem with Angular.
you can use
state.name for state in states track by state.code
Where states in the JSON array, state is the variable name for each object in the array.
Hope this helps
Try it as below:
var scope = $(this).scope();
alert(JSON.stringify(scope.model.options[$('#selOptions').val()].value));

Why does AngularJS include an empty option in select?

I've been working with AngularJS for the last few weeks, and the one thing which is really bothering me is that even after trying all permutations or the configuration defined in the specification at http://docs.angularjs.org/api/ng.directive:select, I still get an empty option as the first child of select element.
Here's the Jade:
select.span9(ng-model='form.type', required, ng-options='option.value as option.name for option in typeOptions');
Here the controller:
$scope.typeOptions = [
{ name: 'Feature', value: 'feature' },
{ name: 'Bug', value: 'bug' },
{ name: 'Enhancement', value: 'enhancement' }
];
Finally, here's the HTML which gets generated:
<select ng-model="form.type" required="required" ng-options="option.value as option.name for option in typeOptions" class="span9 ng-pristine ng-invalid ng-invalid-required">
<option value="?" selected="selected"></option>
<option value="0">Feature</option>
<option value="1">Bug</option>
<option value="2">Enhancement</option>
</select>
What do I need to do to get rid of it?
P.S.: Things work without this as well, but it just looks odd if you use select2 without multiple selection.
The empty option is generated when a value referenced by ng-model doesn't exist in a set of options passed to ng-options. This happens to prevent accidental model selection: AngularJS can see that the initial model is either undefined or not in the set of options and don't want to decide model value on its own.
If you want to get rid of the empty option just select an initial value in your controller, something like:
$scope.form.type = $scope.typeOptions[0].value;
Here is the jsFiddle: http://jsfiddle.net/MTfRD/3/
In short: the empty option means that no valid model is selected (by valid I mean: from the set of options). You need to select a valid model value to get rid of this empty option.
If you want an initial value, see #pkozlowski.opensource's answer, which FYI can also be implemented in the view (rather than in the controller) using ng-init:
<select ng-model="form.type" required="required" ng-init="form.type='bug'"
ng-options="option.value as option.name for option in typeOptions" >
</select>
If you don't want an initial value, "a single hard-coded element, with the value set to an empty string, can be nested into the element. This element will then represent null or "not selected" option":
<select ng-model="form.type" required="required"
ng-options="option.value as option.name for option in typeOptions" >
<option style="display:none" value="">select a type</option>
</select>
Angular < 1.4
For anyone out there that treat "null" as valid value for one of the options (so imagine that "null" is a value of one of the items in typeOptions in example below), I found that simplest way to make sure that automatically added option is hidden is to use ng-if.
<select ng-options="option.value as option.name for option in typeOptions">
<option value="" ng-if="false"></option>
</select>
Why ng-if and not ng-hide? Because you want css selectors that would target first option inside above select to target "real" option, not the one that's hidden. It gets useful when you're using protractor for e2e testing and (for whatever reason) you use by.css() to target select options.
Angular >= 1.4
Due to the refactoring of the select and options directives, using ng-if is no longer a viable option so you gotta turn to ng-show="false" to make it work again.
Maybe useful for someone:
If you want to use plain options instead of ng-options, you could do like below:
<select ng-model="sortorder" ng-init="sortorder='publish_date'">
<option value="publish_date">Ascending</option>
<option value="-publish_date">Descending</option>
</select>
Set the model inline. Use ng-init to get rid of empty option
Something similar was happening to me too and was caused by an upgrade to angular 1.5.ng-init seems to be being parsed for type in newer versions of Angular. In older Angular ng-init="myModelName=600" would map to an option with value "600" i.e. <option value="600">First</option> but in Angular 1.5 it won't find this as it seems to be expecting to find an option with value 600 i.e <option value=600>First</option>. Angular would then insert a random first item:
<option value="? number:600 ?"></option>
Angular < 1.2.x
<select ng-model="myModelName" ng-init="myModelName=600">
<option value="600">First</option>
<option value="700">Second</option>
</select>
Angular > 1.2
<select ng-model="myModelName" ng-init="myModelName='600'">
<option value="600">First</option>
<option value="700">Second</option>
</select>
Among the multitudes of answers here, I figured I'd repost the solution that worked for me and met all of the following conditions:
provided a placeholder/prompt when the ng-model is falsy (e.g. "--select region--" w. value="")
when ng-model value is falsy and user opens the options dropdown, the placeholder is selected (other solutions mentioned here make the first option appear selected which can be misleading)
allow the user to deselect a valid value, essentially selecting the falsy/default value again
code
<select name="market_vertical" ng-model="vc.viewData.market_vertical"
ng-options="opt as (opt | capitalizeFirst) for opt in vc.adminData.regions">
<option ng-selected="true" value="">select a market vertical</option>
</select>
src
original q&a - https://stackoverflow.com/a/32880941/1121919
A quick solution:
select option:empty { display:none }
Hope it helps someone. Ideally, the selected answer should be the approach but if in case that's not possible then should work as a patch.
Yes ng-model will create empty option value, when ng-model property undefined. We can avoid this, if we assign object to ng-model
Example
angular coding
$scope.collections = [
{ name: 'Feature', value: 'feature' },
{ name: 'Bug', value: 'bug' },
{ name: 'Enhancement', value: 'enhancement'}
];
$scope.selectedOption = $scope.collections[0];
<select class='form-control' data-ng-model='selectedOption' data-ng-options='item as item.name for item in collections'></select>
Important Note:
Assign object of array like $scope.collections[0] or $scope.collections[1] to ng-model, dont use object properties. if you are getting select option value from server, using call back function, assign object to ng-model
NOTE from Angular document
Note: ngModel compares by reference, not value. This is important when binding to an array of objects. see an example http://jsfiddle.net/qWzTb/
i have tried lot of times finally i found it.
Though both #pkozlowski.opensource's and #Mark's answers are correct, I'd like to share my slightly modified version where I always select the first item in the list, regardless of its value:
<select ng-options="option.value as option.name for option in typeOptions" ng-init="form.type=typeOptions[0].value">
</select>
I'm using Angular 1.4x and I found this example, so I used ng-init to set the initial value in the select:
<select ng-init="foo = foo || items[0]" ng-model="foo" ng-options="item as item.id for item in items"></select>
I faced the same issue. If you are posting an angular form with normal post then you will face this issue, as angular don't allow you to set values for the options in the way you have used. If you get the value of "form.type" then you will find the right value. You have to post the angular object it self not the form post.
A simple solution is to set an option with a blank value "" I found this eliminates the extra undefined option.
Ok, actually the answer is way simple: when there is a option not recognized by Angular, it includes a dull one.
What you are doing wrong is, when you use ng-options, it reads an object, say [{ id: 10, name: test }, { id: 11, name: test2 }] right?
This is what your model value needs to be to evaluate it as equal, say you want selected value to be 10, you need to set your model to a value like { id: 10, name: test } to select 10, therefore it will NOT create that trash.
Hope it helps everybody to understand, I had a rough time trying :)
This solution works for me:
<select ng-model="mymodel">
<option ng-value="''" style="display:none;" selected>Country</option>
<option value="US">USA</option>
</select>
This worked for me
<select ng-init="basicProfile.casteId" ng-model="basicProfile.casteId" class="form-control">
<option value="0">Select Caste....</option>
<option data-ng-repeat="option in formCastes" value="{{option.id}}">{{option.casteName}}</option>
</select>
This works perfectly fine
<select ng-model="contact.Title" ng-options="co for co in['Mr.','Ms.','Mrs.','Dr.','Prof.']">
<option style="display:none" value=""></option>
</select>
the way it works is, that this gives the first option to be displayed before selecting something and the display:none removes it form the dropdown so if you want you can do
<select ng-model="contact.Title" ng-options="co for co in['Mr.','Ms.','Mrs.','Dr.','Prof.']">
<option style="display:none" value="">select an option...</option>
</select>
and this will give you the select and option before selecting but once selected it will disappear, and it will not show up in the dropdown.
Try this one in your controller, in the same order:
$scope.typeOptions = [
{ name: 'Feature', value: 'feature' },
{ name: 'Bug', value: 'bug' },
{ name: 'Enhancement', value: 'enhancement' }
];
$scope.form.type = $scope.typeOptions[0];
Here is the fix :
for a sample data like :
financeRef.pageCount = [{listCount:10,listName:modelStrings.COMMON_TEN_PAGE},
{listCount:25,listName:modelStrings.COMMON_TWENTYFIVE_PAGE},
{listCount:50,listName:modelStrings.COMMON_FIFTY_PAGE}];
The select option should be like this:-
<select ng-model="financeRef.financeLimit" ng-change="financeRef.updateRecords(1)"
class="perPageCount" ng-show="financeRef.showTable" ng-init="financeRef.financeLimit=10"
ng-options="value.listCount as value.listName for value in financeRef.pageCount"
></select>
The point being when we write value.listCount as value.listName, it automatically populates the text in value.listName but the value of the selected option is value.listCount although the values my show normal 0,1,2 .. and so on !!!
In my case, the financeRef.financeLimit is actually grabbing the value.listCount and I can do my manipulation in the controller dynamically.
I would like to add that if the initial value comes from a binding from some parent element or 1.5 component, make sure that the proper type is passed. If using # in binding, the variable passed will be string and if the options are eg. integers then the empty option will show up.
Either parse properly the value in init, or binding with < and not # (less recommended for performance unless necessary).
Simple solution
<select ng-model='form.type' required><options>
<option ng-repeat="tp in typeOptions" ng-selected="
{{form.type==tp.value?true:false}}" value="{{tp.value}}">{{tp.name}}</option>
A grind solution with jQuery when you haven't the control of the options
html:
<select id="selector" ng-select="selector" data-ng-init=init() >
...
</select>
js:
$scope.init = function () {
jQuery('#selector option:first').remove();
$scope.selector=jQuery('#selector option:first').val();
}
If you use ng-init your model to solve this problem:
<select ng-model="foo" ng-app ng-init="foo='2'">
i had the same problem,
i (removed "ng-model") changed this :
<select ng-model="mapayear" id="mapayear" name="mapayear" style=" display:inline-block !important; max-width: 20%;" class="form-control">
<option id="removable" hidden> Selecione u </option>
<option selected ng-repeat="x in anos" value="{{ x.ano }}">{{ x.ano }}
</option>
</select>
to this:
<select id="mapayear" name="mapayear" style=" display:inline-block !important; max-width: 20%;" class="form-control">
<option id="removable" hidden> Selecione u </option>
<option selected ng-repeat="x in anos" value="{{ x.ano }}">{{ x.ano }}
</option>
</select>
now its working, but in my case it was cause ive deleted that scope from ng.controller, check if u didn't do the same.
The only thing worked for me is using track by in ng-options, like this:
<select class="dropdown" ng-model="selectedUserTable" ng-options="option.Id as option.Name for option in userTables track by option.Id">
Refer the example from angularjs documentation how to overcome these issues.
Go to this documentation link here
Find 'Binding select to a non-string value via ngModel parsing / formatting'
There u can see there, directive called 'convertToNumber' solve the issue.
It works for me. Can also see how it works here
We can use CSS to hide the first option , But it wont work in IE 10, 11. The best way is to remove the element using Jquery. This solution works for major browser tested in chrome and IE10 ,11
Also if you are using angular , sometime using setTimeout works
$scope.RemoveFirstOptionElement = function (element) {
setTimeout(function () {
$(element.children()[0]).remove();
}, 0);
};

Resources