angularjsangularjs-scopehtml-selectng-optionsangularjs-ng-init

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!


Solution

  • 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/