Initializing select with AngularJS and ng-repeat
Asked Answered
E

6

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/

Espresso answered 5/9, 2013 at 22:42 Comment(3)
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 optionsThoughtless
Yes! The ngSelected worked! I will post an updated working JsFiddle.Espresso
It should be noted that in AngualrJS 1.2 this code works differently - It doesn't create a new empty <OPTION> element, however, it is only able to select the first option in the listbox. You can see it hereEngird
N
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

Nabala answered 6/9, 2013 at 5:26 Comment(7)
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, CharlesEspresso
Thanks, that was an easy way to have some options disabled.Pollypollyanna
ng-options is only correct as long as its usecases fit yours. You may need extra attributes/classes in the option/optgroup fields etc.Ethyne
"the correct way" doesn't work sometimes (eg. if you want to translate the strings that populate the option names)Interrogative
I think the ng-selected expression has to be without the {{ }}: ng-selected="operator.value == filterCondition.operator"Spinnaker
This solved my problem, I needed the options to have unique ids and this let me use {{$index}}Dissociate
Great little hack for angular 1.3 where ng-options isn't as sophisticated. Thanks!Hate
W
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>
Wandering answered 5/9, 2013 at 22:57 Comment(1)
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, CharlesEspresso
G
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>
Godewyn answered 10/2, 2015 at 6:49 Comment(1)
This is good, but Angular create an blank option. How to avoid this?Permalloy
E
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'}
     ]
}
Espresso answered 5/9, 2013 at 23:56 Comment(1)
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.Thoughtless
H
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>
Hymnal answered 5/9, 2013 at 22:54 Comment(4)
Do you know why and are you sure? The documentation (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 <option title="A very long description of this choice"...> Thanks.Espresso
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 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?Hymnal
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.Espresso
@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...Quillen
L
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>
Longbow answered 28/7, 2016 at 14:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.