Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 973
code-sushi Avatar asked Aug 25 '14 21:08

code-sushi


2 Answers

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/

like image 153
Yoshi Avatar answered Sep 21 '22 07:09

Yoshi


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

like image 36
code-sushi Avatar answered Sep 20 '22 07:09

code-sushi