672

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sudhanshu
  • 6,893
  • 3
  • 19
  • 10

26 Answers26

656

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.

pkozlowski.opensource
  • 117,202
  • 60
  • 326
  • 286
  • 8
    I tried with both $scope.form.type = ''; and $scope.form.type = $scope.typeOptions[0], however I still se this - – Sudhanshu Sep 29 '12 at 17:37
  • 2
    Sorry, the line with HTML was so long that I haven't noticed that you are binding names, not the full objects. Please try with $scope.typeOptions[0].value, if this still doesn't work I will send a jsFiddle with an example. – pkozlowski.opensource Sep 29 '12 at 17:53
  • 5
    Sometimes it really doesn't make sense to have this data in the JS. If it's the server that made the decision about what was in that select, why should the JS have to duplicate it? – heneryville Feb 06 '13 at 01:06
  • 12
    This unfortunately doesn't work if you are using an array and `null` is one of your values. – vpiTriumph Sep 26 '13 at 02:57
  • You can also add ng-init to the select tag and set the default value there. – brutalhonesty Jan 24 '14 at 21:23
  • 2
    @pkozlowski.opensource What if there is nothing in the list? I shouldn't be able to select anything. What's the right thing to do in that case? – andyczerwonka Apr 27 '14 at 20:34
  • 6
    Is it possible to add a text inside the empty `` – RPDeshaies Nov 15 '14 at 18:07
  • 3
    @Tareck117 just wirte `` thats the null-Value of the optionlist. No need for ?-character. – Sebastian Jan 30 '15 at 12:26
  • 1
    But I still can't understand why undefined model must result in an empty option inside the select's optionlist. If you use a default – Sebastian Jan 30 '15 at 12:29
  • This still doesn't work in case you need to $compile the select object. After the compilation the blank option comes back. Please post here if you find a solution to that. Fiddle here: http://jsfiddle.net/gasbcz5z/5/ – downhand Mar 05 '15 at 13:26
  • Please remember about scopes in this cases! I've spent some time with struggling with similar case and at the end I was set the default value to the object on different scope [ng-repeat] ;) – Przemek Nowak Jun 13 '15 at 15:04
  • What if you add a ng-repeat on an options element instead of using ng-options? – Coded Container Jul 14 '16 at 20:17
  • @pkozlowski.opensource I have still same problem. Nothing helps me for solve this problem. I don't know. It works in local server and other production site, but when I upload this panel with same code snippets to new site , it's not working. – rufatZZ Dec 22 '17 at 14:00
  • Hi @pkozlowski.opensource thank you for your answer, can you please check this question i have plunker and all data to edit, also has bounty https://stackoverflow.com/questions/48355599/loading-large-array-in-oi-select-takes-too-much-of-time-in-angularjs Thank you... – Sudarshan Kalebere Jan 24 '18 at 09:44
241

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>
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • 4
    @hugo, working fiddle: http://jsfiddle.net/4qKyx/1/ Note that "select a type" is shown, but there is no value associated with it. And once you select something else, you won't see it again. (Test on Chrome.) – Mark Rajcok Mar 09 '13 at 21:17
  • This solution works best for me. @pkozlowski.opensource solution works too, however, in my app it also caused products not showing up in the shopping cart after. Thanks! – Paul Jul 12 '13 at 05:40
  • 1
    @MarkRajcok - excellent suggestion! i have stumbled on another issue using your example with transcluding directive. Could you please have a look? http://plnkr.co/edit/efaZdEQlNfoDihk1R1zc?p=preview – Jan-Terje Sørensen Apr 15 '14 at 14:01
  • For some reason the last example "select a type" works but not the first one. I initialised in the controller as well as in the view still it keeps showing me the blank option. – Keshav Agrawal Apr 28 '14 at 08:07
  • Just to pick a little nit ... if you use something other than "value" to specify the value, e.g. "val", you should reference that instead. Otherwise you'll keep coming up with undefineds, and console nasty grams. :) – code-sushi Jul 07 '14 at 21:59
  • 2
    Problem with this is you can still select with keyboard, see http://stackoverflow.com/a/8442831/57344 solution – HaveAGuess Jul 14 '14 at 00:16
  • 2
    This may work for now, but the angular documentation specifically recommends against using ng-init for anything besides ng-repeat - see https://docs.angularjs.org/api/ng/directive/ngInit – Ed Norris Aug 08 '14 at 20:07
  • 3
    Does not work in IE10, the "select a type" is always visible and selectable. – Robin Aug 27 '14 at 07:00
  • I had to use `` with the `null` value instead of an empty string `''`. Nothing else worked. Cheers. (Angular 1.3) – pztrick Oct 02 '14 at 17:42
  • 1
    The golden nugget in this answer is being able to control what the "empty" selection says by supplying a single option. Sometimes you don't want to pre-select an answer. – broc.seib Jul 26 '15 at 14:25
  • 1
    the second one is awesome. I didn't want to go ng-options because it makes using complex filters annoying. Second option just works perfectly with a default value selected – Jan Feb 22 '16 at 15:57
  • @MarkRajcok hi can you please check this question has bounty too https://stackoverflow.com/questions/48355599/loading-large-array-in-oi-select-takes-too-much-of-time-in-angularjs – Sudarshan Kalebere Jan 24 '18 at 09:45
124

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.

WTK
  • 16,583
  • 6
  • 35
  • 45
  • 5
    Should be the accepted answer, because this is the correct visual behavior for the select. You put `size="5"` to the selects attributes and you can see that there is nothing selected! Like in a default ` – Sebastian Jan 30 '15 at 12:24
  • 1
    Yes i agree, this should be the accepted answer. This is the 'angular-way'. ng-if actually removes the options element from the dom (when false) so this solution also works on all browsers without 'hacky' coding (contrary to ng-hide or ng-show). – Sander_P Mar 09 '15 at 13:59
  • 2
    @Sander_P by definition this solution is "hacky" coding in my opinion (putting something in the dom to trick angular into taking it out), but you're right it still is the "best" solution. The "even better" solution would be to allow for a friggin select that has no value! What the heck? Its not like this is an edge scenario. – dudewad Jul 24 '15 at 18:31
  • @dudewad: things might work better with Angular 1.4. From the AngularJS blog for 1.4.0: "ngOptions was completely refactored to allow a number of annoying bugs to be fixed," – Sander_P Jul 27 '15 at 07:05
  • 1
    Haha "annoying bugs". Okay. Well I'm still a little skiddish when it comes to upgrading 3 days before delivery so I'll keep with your solution for now, which seems to be working swimmingly. Noted for the future, though :) Thanks! – dudewad Jul 27 '15 at 16:39
  • This did the trick for me. Thanks!!! But I used in different way, i need to handle with Object so Defiantly required **ng-repeat**. `` – Sai Sep 16 '15 at 07:21
  • God sent you to our lives to lead the way to us sinners my son! – Sotiris Zegiannis Jul 26 '19 at 17:50
36

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

Adam K Dean
  • 7,387
  • 10
  • 47
  • 68
Abraham Jagadeesh
  • 1,757
  • 1
  • 23
  • 21
  • I haven't been able to find a clear example of setting up ng-options with OBJECTS rather than JSON arrays and when I try to "translate" between the two, not only does it not come out right but it fails "silently" so I haven't a clue what I'm doing wrong. I finally opted for plain ng-repeat in an option tag. I know it's not best practice but at least it WORKS. If anyone can point me to a straightforward, easily swappable on the details, angular-n00b-friendly WORKING example using ng-options with data objects (NOT JSON), I'd be elated. Double elated if it uses ng-init as well. – code-sushi Aug 23 '14 at 00:14
  • I am using plain options, but in the controller i dont seem to get the value of selected dropdown. Ex:I tried alert($scope.sortorder.value); and alert($scope.sortorder); both gives undefined. How to get the selected value here? – Maverick Riz Jul 24 '15 at 18:54
  • Thank you so much for this. I was about to write a completely useless object in the controller that I didn't need I just needed a simple select.. Thank you once again. – Muhammad bin Yusrat Jan 18 '17 at 10:36
26

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>
Nabil Boag
  • 460
  • 5
  • 10
  • 2
    Thanks! In my case I can't use ng-init (no default value) so I casted my model to a string and it worked! – Rodrigo Graça Aug 03 '16 at 15:06
  • Very useful to my application production issue. Thanks @Nabil Boag – Venkaiah Yepuri Feb 03 '17 at 06:49
  • 3
    This works, sure, but the better options is to use `ng-value="600"` in the `option` instead of `value`. It will parse it to an number and you dont have to convert your values like you do now :) – Max May 29 '18 at 08:52
  • @Max I tried a lot of high value answers but none worked until I found yours. Thank you. – Andrew Oct 29 '19 at 17:01
23

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

enter image description here

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

Community
  • 1
  • 1
jusopi
  • 6,791
  • 2
  • 33
  • 44
18

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.

K K
  • 17,794
  • 4
  • 30
  • 39
  • 2
    Per my testing this will only work in Chrome (new Chrome, on top of that) and Firefox. IE and Safari break. Don't know about opera and other edge browsers. Either way, this is *not* a good solution, unfortunately. – dudewad Jul 24 '15 at 18:28
  • where should we place that in html before the close of – H Varma Dec 11 '16 at 18:38
  • 1
    @HVarma It's a CSS rule. Place it in the stylesheet or inside – K K Dec 12 '16 at 03:25
  • @KK https://stackoverflow.com/questions/48355599/loading-large-array-in-oi-select-takes-too-much-of-time-in-angularjs Can you please check this? – Sudarshan Kalebere Jan 24 '18 at 09:46
14

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.

Jeff LaFay
  • 12,882
  • 13
  • 71
  • 101
Kanagaraj Vadivel
  • 473
  • 2
  • 7
  • 14
9

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>
denkan
  • 134
  • 1
  • 4
4

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>
James Drinkard
  • 15,342
  • 16
  • 114
  • 137
3


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.

Jitu
  • 635
  • 4
  • 7
3

A simple solution is to set an option with a blank value "" I found this eliminates the extra undefined option.

Undo
  • 25,519
  • 37
  • 106
  • 129
kgrondell
  • 177
  • 2
  • 8
3

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 :)

Marco
  • 2,757
  • 1
  • 19
  • 24
2

This solution works for me:

<select ng-model="mymodel">    
   <option ng-value="''" style="display:none;" selected>Country</option>
   <option value="US">USA</option>
</select>
MMM
  • 103
  • 13
  • Downvote could be because applying CSS to option elements doesn't work across all browsers. – Kafoso Oct 05 '18 at 11:03
  • I just tested it on Chrome, FireFox, Safari and Edge - everything is working. – MMM Oct 10 '18 at 10:32
  • @MMM "all browsers" includes IE. A lot of users are still stuck with IE 10 and 11, and that's likely why the downvote happened. Chrome, fF, Saf, and Edge aren't "all browsers". – PKD Apr 24 '20 at 15:49
1

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>
Nayas Subramanian
  • 2,269
  • 21
  • 28
1

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.

David Genger
  • 875
  • 10
  • 25
  • Actually you don't answer the question itself, worst than that, you are just hiding the element. – Marco Sep 26 '16 at 18:47
0

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];
user
  • 86,916
  • 18
  • 197
  • 190
Macfer Ann
  • 153
  • 1
  • 9
  • I tried your method but unfortunately I couldn't get it to work. Can you take a look at this fiddle. http://jsfiddle.net/5PdaX/ – Aniket Sinha Jun 27 '14 at 06:20
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.

Edward
  • 3,292
  • 1
  • 27
  • 38
lisapanda
  • 53
  • 3
0

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).

Wtower
  • 18,848
  • 11
  • 103
  • 80
0

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>

Gajendra Kumar
  • 908
  • 2
  • 12
  • 28
0

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();
}
Pascal KOCH
  • 109
  • 1
  • 2
0

If you use ng-init your model to solve this problem:

<select ng-model="foo" ng-app ng-init="foo='2'">
luispa
  • 133
  • 3
  • 7
0

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.

Kuza Grave
  • 1,256
  • 14
  • 15
0

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">

Dipendu Paul
  • 2,685
  • 1
  • 23
  • 20
  • if I use this `ng-option` get last value of array which I want to set value of `option` of `select`. It did not solved :( – rufatZZ Dec 22 '17 at 13:26
0

Refer the example from angularjs documentation how to overcome these issues.

  1. Go to this documentation link here
  2. Find 'Binding select to a non-string value via ngModel parsing / formatting'
  3. There u can see there, directive called 'convertToNumber' solve the issue.

It works for me. Can also see how it works here

ihsanberahim
  • 1,051
  • 12
  • 14
0

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);
};