137

I'm trying to get select-box to start off with a pre-filled option using ng-repeat with AngularJS 1.1.5. Instead the select always starts out with nothing selected. It also has an empty option, which I don't want. I think that there is a side effect of nothing being selected.

I can get this working using ng-options instead of ng-repeat, but I want to use ng-repeat for this case. Although my narrowed down example doesn't show it, I also want to set the title attribute of each option, and there is no way to do that using ng-options, as far as I know.

I don't think this is related to the common AngularJs scope/prototypical inheritance issue. At least I don't see anything obvious when inspecting in Batarang. Plus, when you pick an option in the select with the UI, the model does update correctly.

Here's the HTML:

<body ng-app ng-controller="AppCtrl">
    <div>
        Operator is: {{filterCondition.operator}}
    </div>
    <select ng-model="filterCondition.operator">
       <option 
           ng-repeat="operator in operators" 
           value="{{operator.value}}"
       >
           {{operator.displayName}}
       </option>
    </select>
</body>

And the JavaScript:

function AppCtrl($scope) {

    $scope.filterCondition={
        operator: 'eq'
    }

    $scope.operators = [
        {value: 'eq', displayName: 'equals'},
        {value: 'neq', displayName: 'not equal'}
     ]
}

JS Fiddle for this : http://jsfiddle.net/coverbeck/FxM3B/2/

Kailas
  • 7,350
  • 3
  • 47
  • 63
Charles O.
  • 2,237
  • 3
  • 17
  • 21
  • 1
    ngOption was created because there was no clean way to do this with ngRepeat. ngRepeat unrolls after the option has been selected. Have you tried setting ngSelect on your options – TheSharpieOne Sep 05 '13 at 22:51
  • Yes! The ngSelected worked! I will post an updated working JsFiddle. – Charles O. Sep 05 '13 at 23:44
  • It should be noted that in AngualrJS 1.2 this code works differently - It doesn't create a new empty – VitalyB May 18 '14 at 12:54

6 Answers6

233

OK. If you don't want to use the correct way ng-options, you can add ng-selected attribute with a condition check logic for the option directive to to make the pre-select work.

<select ng-model="filterCondition.operator">
    <option ng-selected="{{operator.value == filterCondition.operator}}"
            ng-repeat="operator in operators"
            value="{{operator.value}}">
      {{operator.displayName}}
    </option>
</select>

Working Demo

Dorian
  • 22,759
  • 8
  • 120
  • 116
zs2020
  • 53,766
  • 29
  • 154
  • 219
  • 38
    I'll give you the credit since you figured it out on your own and did the full example. Although I don't agree with your comment about ng-options being the "correct way." :) I have always used ng-options myself, but this required doing something that ng-options doesn't support, small as it is. And the Angular doc says you can juse ng-repeat as well. Thanks, Charles – Charles O. Sep 06 '13 at 20:49
  • Thanks, that was an easy way to have some options disabled. – Dorian Nov 25 '13 at 21:03
  • 12
    ng-options is only correct as long as its usecases fit yours. You may need extra attributes/classes in the option/optgroup fields etc. – zrooda Oct 15 '14 at 16:34
  • 7
    "the correct way" doesn't work sometimes (eg. if you want to translate the strings that populate the option names) – btk Mar 16 '15 at 21:10
  • 11
    I think the ng-selected expression has to be without the {{ }}: ng-selected="operator.value == filterCondition.operator" – mvermand Jun 17 '15 at 04:54
  • This solved my problem, I needed the options to have unique ids and this let me use `{{$index}}` – usumoio Jul 09 '15 at 18:11
  • Great little hack for angular 1.3 where ng-options isn't as sophisticated. Thanks! – radtek Jul 28 '16 at 19:47
36

For the select tag, angular provides the ng-options directive. It gives you the specific framework to set up options and set a default. Here is the updated fiddle using ng-options that works as expected: http://jsfiddle.net/FxM3B/4/

Updated HTML (code stays the same)

<body ng-app ng-controller="AppCtrl">
<div>Operator is: {{filterCondition.operator}}</div>
<select ng-model="filterCondition.operator" ng-options="operator.value as operator.displayName for operator in operators">
</select>
</body>
Jeremy Likness
  • 7,531
  • 1
  • 23
  • 25
  • 3
    Hi Jeremy -- I'm trying to do this without ng-options. Like I said in my question and in reply to Shaun, I would like to set the title attribute on each option, and I can't do that with ng-options, as far as I know. Thanks, Charles – Charles O. Sep 05 '13 at 23:25
10

The fact that angular is injecting an empty option element to the select is that the model object binded to it by default comes with an empty value in when initialized.

If you want to select a default option then you can probably can set it on the scope in the controller

$scope.filterCondition.operator = "your value here";

If you want to an empty option placeholder, this works for me

<select ng-model="filterCondition.operator" ng-options="operator.id as operator.name for operator in operators">
  <option value="">Choose Operator</option>
</select>
Ammadu
  • 1,675
  • 15
  • 17
9

Thanks to TheSharpieOne for pointing out the ng-selected option. If that had been posted as an answer rather than as a comment, I would have made that the correct answer.

Here's a working JSFiddle: http://jsfiddle.net/coverbeck/FxM3B/5/.

I also updated the fiddle to use the title attribute, which I had left out in my original post, since it wasn't the cause of the problem (but it is the reason I want to use ng-repeat instead of ng-options).

HTML:

<body ng-app ng-controller="AppCtrl">
<div>Operator is: {{filterCondition.operator}}</div>
<select ng-model="filterCondition.operator">
   <option ng-repeat="operator in operators" title="{{operator.title}}" ng-selected="{{operator.value == filterCondition.operator}}" value="{{operator.value}}">{{operator.displayName}}</option>
</select>
</body>

JS:

function AppCtrl($scope) {

    $scope.filterCondition={
        operator: 'eq'
    }

    $scope.operators = [
        {value: 'eq', displayName: 'equals', title: 'The equals operator does blah, blah'},
        {value: 'neq', displayName: 'not equal', title: 'The not equals operator does yada yada'}
     ]
}
Community
  • 1
  • 1
Charles O.
  • 2,237
  • 3
  • 17
  • 21
  • Sorry about posting as a comment. I didn't feel like making an example and I wasn't 100% sure it would work. It was just a hunch. – TheSharpieOne Sep 06 '13 at 01:04
1

As suggested you need to use ng-options and unfortunately I believe you need to reference the array element for a default (unless the array is an array of strings).

http://jsfiddle.net/FxM3B/3/

The JavaScript:

function AppCtrl($scope) {


    $scope.operators = [
        {value: 'eq', displayName: 'equals'},
        {value: 'neq', displayName: 'not equal'}
     ]

    $scope.filterCondition={
        operator: $scope.operators[0]
    }
}

The HTML:

<body ng-app ng-controller="AppCtrl">
<div>Operator is: {{filterCondition.operator.value}}</div>
<select ng-model="filterCondition.operator" ng-options="operator.displayName for operator in operators">
</select>
</body>
shaunhusain
  • 19,630
  • 4
  • 38
  • 51
  • Do you know why and are you sure? The documentation (http://docs.angularjs.org/api/ng.directive:select) says you only need to use ng-options when binding to a non-string value. Like I said I want to use the title attribute, and there is no way to do that with ng-options. Something like – Charles O. Sep 05 '13 at 23:00
  • I believe the note in the docs is a bit unclear and actually means you would have to be using a string for the model and have an array of strings for the options. If you don't mind what's the reasoning for using the "title" attribute I don't see it on W3C http://www.w3schools.com/tags/tag_option.asp and can't recall using it really. Couldn't you just store the description in the objects then still retrieve that data since that's what the model is now bound to? – shaunhusain Sep 05 '13 at 23:06
  • On the W3C page you linked to, if you click on the Global Attributes link, you'll see that the title attribute is supported by the option element. I'm using the title attribute just to display the "tooltip" text, so that if you hover over an option, you can get a bigger description of what it the option means. It's a small thing, but it would be nice to have, if possible. – Charles O. Sep 05 '13 at 23:31
  • @CharlesO. I want to do exactly like you. Set an object (non-string value) but I also want to use a title in the option list. Is there a solution for this. I cannot seem to find it... – Wilt Jun 20 '16 at 09:41
1

If you are using md-select and ng-repeat ing md-option from angular material then you can add ng-model-options="{trackBy: '$value.id'}" to the md-select tag ash shown in this pen

Code:

<md-select ng-model="user" style="min-width: 200px;" ng-model-options="{trackBy: '$value.id'}">
  <md-select-label>{{ user ? user.name : 'Assign to user' }}</md-select-label>
  <md-option ng-value="user" ng-repeat="user in users">{{user.name}}</md-option>
</md-select>