AngularJS select: remove empty option and use data objects not arrays
Asked Answered
F

4

6

I've been searching high and low for an explanation as to how to remove the empty option AngularJS always renders in selects. I have found lots of information doing this when the options are derived from a JSON array placed directly in the code, but I can't find anything about removing this empty option when working with data objects.

Say I have object "foo" in my database and "foo" has both a "name" and a "bar_id".

Can someone toss me a clue about doing this with data objects?

References: Angular JS Remove Blank option from Select Option

http://jsfiddle.net/MTfRD/3/ (This is someone else's fiddle from the SO question linked above; code is not mine but taken from that fiddle.)

JS:

function MyCtrl($scope) {
    $scope.typeOptions = [

        { name: 'Feature', value: 'feature' }, 
        { name: 'Bug', value: 'bug' }, 
        { name: 'Enhancement', value: 'enhancement' }
    ];

    $scope.form = {type : $scope.typeOptions[0].value};
}

I want to get foo.name and foo.bar_id. I want foo.name to be the option label and foo.bar_id to be the value (e.g. <option value="foo.bar_id">foo.name</option>. For each foo, foo.bar_id will then become an identifying parameter in the record to pull up and display "on change" (immediately upon selection of the option).

I've tried everything I can think of to set this up and nothing seems to work. Worst of all it either fails silently, leaving me with no indication what I am doing wrong, or complains that foo is not an object, which frustrates me to no end. (I assure you, "foo" IS being loaded by an AJAX request in the actual code I'm working on and works fine as an object everywhere else -- NDAs prohibit me from sharing actual code though, sorry.)

Using the above example from the other thread, I understand I would assign something like ng-model="typeOptions" in the select tag within the template, but how would I get "typeOptions" to access foo and make foo.name the option label and foo.bar_id the option value? Also, default angular values for the options (the ones that look like a generated index) are fine with me, but I still need to call upon foo.bar_id to get the task done, so it has to be "in there somewhere".

Any help would be appreciated; currently I'm stuck with the hackish ng-repeat in the option itself, which I understand is not best practice.. Also I'm still fairly new at Angular, and still find it somewhat confusing, so the simpler and more straightforward your answers, the better I will be able to use them successfully. Thanks!

Fowler answered 25/8, 2014 at 21:36 Comment(4)
With ngOptions you can't force angular to use your model-data as the actual option-value in the view. Angular handles this in it's own way. It only assures that your scope-model will be set to the correct value on change. If that's not what you want, than you'll have to write your own directive, probably using ngRepeat.Terresaterrestrial
That's fine; I don't have to have "foo.bar_id" as the option value; I just have to have it available so when the user selects an option it is inserted into a corresponding url parameter to update the display.Fowler
My main concern is what goes in $scope.typeOptions (using the example from the other thread above) INSTEAD OF an array, to get foo.name and foo.bar_id key-value pairs from the data object foo. I have tried putting <select ng-model='fooOptions' ng-options='foo.name as foo.bar_id for foo in fooOptions'></select> but it does not work -- nothing shows up. When I use those as an ng-repeat in the option tag it works (foo.name and foo.bar_id) so the problem isn't with the object properties themselves.Fowler
I see, no you'll always need two seperate models. One which acts as the list of options (this should probably always be an array of objects) and one to store the selected value (this will then differ in regards to how you set up the ngOptions directive).Terresaterrestrial
T
10

update

Ok, I just now understood what your real question was (at least I hope so ;)). And fortunately this is very easy:

Ctrl:

$scope.options = {
  a: 1,
  b: 2,
  c: 3      
};

$scope.selected = 1;

View:

<select
  data-ng-model="selected"
  data-ng-options="v as k for (k, v) in options"
></select>

Demo: http://jsbin.com/hufehidotuca/1/

for more options, have a look at the manual. Especially under:

ngOptions (optional) » for object data sources:


previous answer:

As I wrote in the comments, with ngOptions:

  1. you can't force angular to use your model-data as the actual option-value in the view. Angular handles this in it's own way. It only assures that your scope-model will be set to the correct value on change. If that's not what you want, then you'll have to write your own directive, probably using ngRepeat.

  2. you'll always need two seperate models. One which acts as the list of options (this should probably always be an array of objects) and one to store the selected value (this will then differ in regards to how you set up the ngOptions directive).

  3. Hint: The notorious blank options is always the result when angular can't match the assigned ngModel to the list of options and no real blank option is setup. (Note: if the ngModel is just undefined in the current scope or it's value can't be matched against the list of options, then it will be set or it's value will be overridden, with the first selection of any option. This is why the blank option disappears afterwards.)


Ctrl:

// the "list of options"
$scope.options = [
  { name: 'A', id: 1 },
  { name: 'B', id: 2 },
  { name: 'C', id: 3 },
  { name: 'D', id: 4 }
];

// a pre-selection by value
$scope.asValue  = 2;

// a pre-selection by object-identity
$scope.asObject = $scope.options[3];

View:

<!-- select as value -->
<select
  data-ng-model="asValue"
  data-ng-options="foo.id as foo.name for foo in options"
></select>

<!-- select as object -->
<select
  data-ng-model="asObject"
  data-ng-options="foo as foo.name for foo in options"
></select>

<!-- the notorious blank option -->
<select
  data-ng-model="asBogus"
  data-ng-options="foo as foo.name for foo in options"
></select>

<!-- blank option correctly set up -->
<select
  data-ng-model="asBogus"
  data-ng-options="foo as foo.name for foo in options"
>
  <option value="">Please select</option>
</select>

demo:

http://jsbin.com/yasodacomadu/1/

Terresaterrestrial answered 26/8, 2014 at 7:6 Comment(7)
I don't mean to be a pain and I appreciate your input, but I need help figuring out how to set up a model to work with data objects rather than JSON arrays. I don't get a choice here. There are a "gazillion" records in the database and the API on the back end runs on SQL queries. I understand how to set the pre-selection. Using your example above, how would $scope.options be presented as a data object referencing its properties rather than a static JSON array? That's the main point I need help with; the rest I can grasp.Fowler
OK, again, with $scope.options you are using an array. $scope.options = [{ name: 'A', id: 1 }, { name: 'B', id: 2 }, { name: 'C', id: 3 }, { name: 'D', id: 4 }]; So what would it look like with data objects/properties? Would it be something like $scope.options = ._each(foo, [{ name: foo.foo_name, id: foo.foo_id } ... _etc._ ]); ??? Or what??? Still can't figure out what "goes" there ... if anything ... ?Fowler
@Fowler Please only look at the update section. There I use $scope.options = { a: 1, b: 2, c: 3 };. I left the previous answer just for completeness.Terresaterrestrial
OK so you are saying it would be something like this (with data object foo containing bar and properties name and bar_id -- I realize I didn't do that correctly before it should be like each/every bar in foo.bar getting bar.name and bar.bar_id ... $scope.options = { bar.name: bar.bar_id }; And then the ng-options: data-ng-options="bar.bar_id as bar.name for bar in options" but where & how do I get it to grab each bar in foo.bar to get all those bars and their properties? Do I use angular.foreach? Do I use a standard for loop? I'm still confused about that part, sorry to be a pest!Fowler
I'm going to go ahead and accept your answer since I should not have combined two questions into one -- you DID answer (in several ways) how to get rid of the empty element and I appreciate that.Fowler
@Fowler To be honest, I do have my problems understanding your data-structure. For there are really only two options, an array of objects or one object with properties and values. So, if possible, add a sample of your data to the question and we'll surely find a solution.Terresaterrestrial
I am little bit confused does it uses index of select or value of selectGiustino
F
1

Just posting as a follow up to the whole data object issue ...

To make this work, I had to get the ng-options properly designated first of all. I've mentioned elsewhere that one of my recurring personal issues learning AngularJS is "overthinking" things and confusing myself unnecessarily, and this was no exception. After conferring with a co-worker who is scripting the controller on this app, using my "foo.bar" pseudo-designations, this is how I needed to set up the HTML template:

<select ng-model="selected_item" ng-options="bar.item_name for bar in foo.bars">

I wanted this select to be set to the "bar" of the current display (as opposed to the first indexed "bar" in the complete list of "bars" this "foo" has), so I had to locate the place in the app controller where this data object is being passed in and add for the model (which I will call $selected_item):

$scope.selected_item = _.find($scope.foo.bars, function(bar) {
    return bar.item_id == $scope.current_result.foo_item_id;
});

It's a pretty complicated data structure with a lot of stored procedures and concatenated tables, so my bad for not being able to translate it easily into a working pseudo-object/properties example. Yoshi you have been very helpful; the gaps in communication here have all been my baddies. Thank you for sticking with me.

For other confused AngularJS n00bs who might stumble upon this thread: the place I was "overthinking" it and confusing myself was by mistakenly believing that I needed some sort of code in place of the JSON arrays the many examples I studied, to pass in the data object. That is incorrect -- "nothing" needs to go there with regard to passing in a data object and/or its properties. The data object's properties are passed directly into the template, within the select tag itself, using ng-options. So if anyone else finds themselves stumped by this, you're probably making the same two mistakes I made: (1) thinking "something else" should replace the JSON arrays examples online are using and (2) your ng-options are not written correctly.

Angular sure makes me feel stupid sometimes!! It's fun and challenging but sometimes it just leaves me feeling like an idiot. :-)

Fowler answered 27/8, 2014 at 21:41 Comment(1)
Yes we used a little underscore there. ;-)Fowler
K
1

<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>
Karyogamy answered 20/5, 2015 at 11:8 Comment(0)
H
0

While Using Css we removing the Empty Option like

<option value="?undefind"></option>

So While Usiing Css we can remove the emptyy Options.

Shown in this fiddle

http://jsfiddle.net/KN9xx/1060/

Halfway answered 7/12, 2016 at 5:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.