Travailler avec select en utilisant les options ng d'AngularJS

442

J'ai lu à ce sujet dans d'autres articles, mais je n'ai pas pu le comprendre.

J'ai un tableau,

$scope.items = [
   {ID: '000001', Title: 'Chicago'},
   {ID: '000002', Title: 'New York'},
   {ID: '000003', Title: 'Washington'},
];

Je veux le rendre comme:

<select>
  <option value="000001">Chicago</option>
  <option value="000002">New York</option>
  <option value="000003">Washington</option>
</select>

Et aussi je veux sélectionner l'option avec ID = 000002.

J'ai lu select et essayé, mais je ne peux pas le comprendre.

Andrej Kaurin
la source
Je recommande fortement d'utiliser Select2 , car il s'en occupera pour vous. Il existe même une directive pour AngularJS .
hewstone
En fait, il existe une solution AngularJS pure développée par QuantumUI . Vous pouvez trouver plus d'exemples et de documentations sur http://quantumui.org/ .
Ramon Drunce

Réponses:

799

Une chose à noter est que ngModel est nécessaire pour que ngOptions fonctionne ... notez le ng-model="blah"qui dit "définissez $ scope.blah sur la valeur sélectionnée".

Essaye ça:

<select ng-model="blah" ng-options="item.ID as item.Title for item in items"></select>

Voici plus de la documentation d'AngularJS (si vous ne l'avez pas vu):

pour les sources de données du tableau:

  • étiquette pour la valeur dans le tableau
  • sélectionner comme étiquette pour la valeur dans le tableau
  • étiquette groupe par groupe pour la valeur dans le tableau = sélectionner comme étiquette groupe par groupe pour la valeur dans le tableau

pour les sources de données d'objets:

  • étiquette pour (clé, valeur) dans l'objet
  • sélectionner comme étiquette pour (clé, valeur) dans l'objet
  • étiqueter groupe par groupe pour (clé, valeur) dans l'objet
  • sélectionner comme groupe d'étiquettes par groupe pour (clé, valeur) dans l'objet

Pour des éclaircissements sur les valeurs des balises d'option dans AngularJS:

Lorsque vous utilisez ng-options, les valeurs des balises d'option écrites par ng-options seront toujours l'index de l'élément de tableau auquel la balise d'option se rapporte . En effet, AngularJS vous permet en fait de sélectionner des objets entiers avec des contrôles de sélection, et pas seulement des types primitifs. Par exemple:

app.controller('MainCtrl', function($scope) {
   $scope.items = [
     { id: 1, name: 'foo' },
     { id: 2, name: 'bar' },
     { id: 3, name: 'blah' }
   ];
});
<div ng-controller="MainCtrl">
   <select ng-model="selectedItem" ng-options="item as item.name for item in items"></select>
   <pre>{{selectedItem | json}}</pre>
</div>

Ce qui précède vous permettra de sélectionner $scope.selectedItemdirectement un objet entier . Le fait est qu'avec AngularJS, vous n'avez pas à vous soucier du contenu de votre balise d'option. Laissez AngularJS gérer cela; vous ne devez vous soucier que de ce qui se trouve dans votre modèle dans votre champ d'application.

Voici un plongeur démontrant le comportement ci-dessus et montrant le code HTML écrit


Gérer l'option par défaut:

Il y a quelques choses que je n'ai pas mentionnées ci-dessus concernant l'option par défaut.

Sélection de la première option et suppression de l'option vide:

Vous pouvez le faire en ajoutant un simple ng-initqui définit le modèle (de ng-model) au premier élément dans les éléments dans lesquels vous répétez ng-options:

<select ng-init="foo = foo || items[0]" ng-model="foo" ng-options="item as item.name for item in items"></select>

Remarque: Cela pourrait devenir un peu fou s'il foose trouve qu'il est correctement initialisé à quelque chose de "faux". Dans ce cas, vous voudrez fooprobablement gérer l'initialisation de votre contrôleur.

Personnalisation de l'option par défaut:

C'est un peu différent; ici, tout ce que vous devez faire est d'ajouter une balise d'option en tant qu'enfant de votre sélection, avec un attribut de valeur vide, puis de personnaliser son texte intérieur:

<select ng-model="foo" ng-options="item as item.name for item in items">
   <option value="">Nothing selected</option>
</select>

Remarque: Dans ce cas, l'option "vide" restera là même après avoir sélectionné une option différente. Ce n'est pas le cas pour le comportement par défaut des sélections sous AngularJS.

Une option par défaut personnalisée qui se cache après une sélection:

Si vous vouliez que votre option par défaut personnalisée disparaisse après avoir sélectionné une valeur, vous pouvez ajouter un attribut ng-hide à votre option par défaut:

<select ng-model="foo" ng-options="item as item.name for item in items">
   <option value="" ng-if="foo">Select something to remove me.</option>
</select>
Ben Lesh
la source
3
Il s'avère que ce sont les indices des valeurs, voilà comment angulaire peut vous permettre d'utiliser des objets comme valeur de votre boîte de sélection. Angular fait beaucoup de choses pour vous dans les coulisses avec des boîtes de sélection, et vous n'êtes pas censé vous soucier de l'attribut value de vos options.
Ben Lesh
1
La documentation est sous "select" sur leur site: docs.angularjs.org/api/ng.directive:select
Ben Lesh
25
"... ngModel est nécessaire pour que ngOptions fonctionne ..." était le nœud du problème pour moi. Bonne réponse.
tristanm
1
Est-il possible d'éviter la première option vide ( <option value="?" selected="selected"></option>)?
swenedo
4
J'essaie d'utiliser le dernier cas ( une option par défaut personnalisée qui se cache après une sélection ), mais j'ai trouvé des problèmes. Au lieu de, ng-if="foo"j'ai dû utiliser ng-if=!foopour masquer l'option vide par défaut lorsqu'une autre option est sélectionnée. De plus, l'option vide par défaut apparaît toujours en bas de la liste combinée. Comment puis-je le mettre au début de la liste combinée?
Fran Herrero
90

J'apprends AngularJS et je me débattais aussi avec la sélection. Je sais que cette question a déjà reçu une réponse, mais je voulais néanmoins partager un peu plus de code.

Dans mon test, j'ai deux zones de liste: les marques de voitures et les modèles de voitures. La liste des modèles est désactivée jusqu'à ce qu'une marque soit sélectionnée. Si la sélection dans la liste de marques est ultérieurement réinitialisée (définie sur «Sélectionner la marque»), la liste de modèles est à nouveau désactivée ET sa sélection est également réinitialisée (sur «Sélectionner le modèle»). Les marques sont récupérées en tant que ressource tandis que les modèles sont simplement codés en dur.

Rend JSON:

[
{"code": "0", "name": "Select Make"},
{"code": "1", "name": "Acura"},
{"code": "2", "name": "Audi"}
]

services.js:

angular.module('makeServices', ['ngResource']).
factory('Make', function($resource){
    return $resource('makes.json', {}, {
        query: {method:'GET', isArray:true}
    });
});

Fichier HTML:

<div ng:controller="MakeModelCtrl">
  <div>Make</div>
  <select id="makeListBox"
      ng-model="make.selected"
      ng-options="make.code as make.name for make in makes"
      ng-change="makeChanged(make.selected)">
  </select>

  <div>Model</div>
  <select id="modelListBox"
     ng-disabled="makeNotSelected"
     ng-model="model.selected"
     ng-options="model.code as model.name for model in models">
  </select>
</div>

controllers.js:

function MakeModelCtrl($scope)
{
    $scope.makeNotSelected = true;
    $scope.make = {selected: "0"};
    $scope.makes = Make.query({}, function (makes) {
         $scope.make = {selected: makes[0].code};
    });

    $scope.makeChanged = function(selectedMakeCode) {
        $scope.makeNotSelected = !selectedMakeCode;
        if ($scope.makeNotSelected)
        {
            $scope.model = {selected: "0"};
        }
    };

    $scope.models = [
      {code:"0", name:"Select Model"},
      {code:"1", name:"Model1"},
      {code:"2", name:"Model2"}
    ];
    $scope.model = {selected: "0"};
}
mp31415
la source
27
Cher utilisateur aléatoire. Bien que cette question et sa réponse soient plutôt simples et simples, votre organisation du code dans ses emplacements appropriés et respectifs (contrôleur, service, modèle, données) montre l'élégance d'AngularJS sous sa forme la plus simple et par défaut. Exemple génial.
Atticus
1
Ce n'est pas ainsi qu'il doit être utilisé. ng-modeldevrait pointer vers une autre variable sur votre portée, sans rapport avec make. Voir l'exemple dans docs.angularjs.org/api/ng/directive/ngOptions
Dmitri Zaitsev
@DmitriZaitsev Je pense que vous vous trompez, simplement parce que l'exemple de documentation angulaire le montre comme vous l'avez décrit ne signifie pas que c'est la seule façon. Montrez-nous pourquoi vous pensez qu'il n'est pas censé être utilisé de cette façon plutôt que de dénigrer un excellent exemple pour les débutants.
JRT
39

Pour une raison quelconque, AngularJS permet de m'embrouiller. Leur documentation est assez horrible à ce sujet. De plus bons exemples de variations seraient les bienvenus.

Quoi qu'il en soit, j'ai une légère variation sur la réponse de Ben Lesh.

Mes collections de données ressemblent à ceci:

items =
[
   { key:"AD",value:"Andorra" }
,  { key:"AI",value:"Anguilla" }
,  { key:"AO",value:"Angola" }
 ...etc..
]

Maintenant

<select ng-model="countries" ng-options="item.key as item.value for item in items"></select>

la valeur des options était toujours l'index (0, 1, 2, etc.).

Ajouter un morceau en le corrigeant pour moi:

<select ng-model="blah" ng-options="item.value for item in items track by item.key"></select>

Je pense qu'il arrive plus souvent que vous souhaitiez ajouter un tableau d'objets dans une liste de sélection, donc je vais me souvenir de celui-ci!

Sachez que depuis AngularJS 1.4, vous ne pouvez plus utiliser ng-options, mais vous devez utiliser ng-repeatsur votre balise option:

<select name="test">
   <option ng-repeat="item in items" value="{{item.key}}">{{item.value}}</option>
</select>
Mattijs
la source
3
Cette réponse semble être exactement ce que la question demande. Toutes les autres réponses disent au lecteur de ne pas s'inquiéter de ce que HTML est généré, mais si l'élément select est sous une forme, je m'inquiète beaucoup. Très bonne réponse!
Keith
3
Il convient de noter que cela stocke l'objet complet sélectionné dans le modèle, pas seulement la clé. Si vous voulez juste la clé dans le modèle, vous voulez utiliser la version "item.key as item.value". Cela m'a dérouté pendant un certain temps car j'étais accroché à quoi ressemblait le HTML, pas aux données que je voulais dans le modèle, ce qui est, pour moi, l'important.
mhenry1384
@ mhenry1384 Oui, vous avez raison, à propos du stockage de l'ensemble de l'objet. J'aime vraiment cette fonctionnalité, car elle vous donne accès à plus que l'ID (si vous en avez besoin). Fonctionne bien pour moi lorsque je remplis des listes avec une collection mongo et que j'ai besoin de certaines propriétés de l'élément sélectionné.
Mattijs
2
Et comment pouvons-nous détecter les changements avec "ng-change" à l'intérieur option?
Konstantinos Natsios
2
En ce qui concerne la mise à jour, cela ne semble pas correct. ng-options fonctionne toujours bien en 1.5. Il est également toujours affiché dans la documentation. docs.angularjs.org/api/ng/directive/select
Jeremy A. West
15

La question est déjà répondue (BTW, réponse vraiment bonne et complète fournie par Ben), mais je voudrais ajouter un autre élément pour être complet, qui peut également être très pratique.

Dans l'exemple suggéré par Ben:

<select ng-model="blah" ng-options="item.ID as item.Title for item in items"></select>

ce qui suit ngOptions forme a été utilisée: select as label for value in array.

Label est une expression dont le résultat sera le label de l' <option>élément. Dans ce cas, vous pouvez effectuer certaines concaténations de chaînes, afin d'avoir des étiquettes d'options plus complexes.

Exemples:

  • ng-options="item.ID as item.Title + ' - ' + item.ID for item in items" vous donne des étiquettes comme Title - ID
  • ng-options="item.ID as item.Title + ' (' + item.Title.length + ')' for item in items"vous donne des étiquettes comme Title (X), où Xest la longueur de la chaîne de titre.

Vous pouvez également utiliser des filtres, par exemple,

  • ng-options="item.ID as item.Title + ' (' + (item.Title | uppercase) + ')' for item in items"vous donne des étiquettes comme Title (TITLE), où la valeur Title de la propriété Title et TITLE est la même valeur mais convertie en caractères majuscules.
  • ng-options="item.ID as item.Title + ' (' + (item.SomeDate | date) + ')' for item in items"vous donne des étiquettes comme Title (27 Sep 2015), si votre modèle a une propriétéSomeDate
À M
la source
7

Dans CoffeeScript:

#directive
app.directive('select2', ->
    templateUrl: 'partials/select.html'
    restrict: 'E'
    transclude: 1
    replace: 1
    scope:
        options: '='
        model: '='
    link: (scope, el, atr)->
        el.bind 'change', ->
            console.log this.value
            scope.model = parseInt(this.value)
            console.log scope
            scope.$apply()
)
<!-- HTML partial -->
<select>
  <option ng-repeat='o in options'
          value='{{$index}}' ng-bind='o'></option>
</select>

<!-- HTML usage -->
<select2 options='mnuOffline' model='offlinePage.toggle' ></select2>

<!-- Conclusion -->
<p>Sometimes it's much easier to create your own directive...</p>
Akatsuki Sai
la source
1
N'oubliez pas votre radixpour parseInt: http://davidwalsh.name/parseint-radix
GFoley83
6

Si vous avez besoin d'un titre personnalisé pour chaque option, ng-optionsn'est pas applicable. Utilisez plutôt ng-repeatavec des options:

<select ng-model="myVariable">
  <option ng-repeat="item in items"
          value="{{item.ID}}"
          title="Custom title: {{item.Title}} [{{item.ID}}]">
       {{item.Title}}
  </option>
</select>
Dmitri Algazin
la source
3

J'espère que ce qui suit fonctionnera pour vous.

<select class="form-control"
        ng-model="selectedOption"
        ng-options="option.name + ' (' + (option.price | currency:'USD$') + ')' for option in options">
</select>
Sonar Meghshyam
la source
1

Cela peut être utile. Les liaisons ne fonctionnent pas toujours.

<select id="product" class="form-control" name="product" required
        ng-model="issue.productId"
        ng-change="getProductVersions()"
        ng-options="p.id as p.shortName for p in products"></select>

Par exemple, vous remplissez le modèle source de liste d'options à partir d'un service REST. Une valeur sélectionnée était connue avant de remplir la liste et elle a été définie. Après avoir exécuté la requête REST avec $ http, l'option de liste est terminée.

Mais l'option sélectionnée n'est pas définie. Pour des raisons inconnues, AngularJS dans l'exécution de shadow $ digest ne lie pas la sélection comme il se doit. Je dois utiliser jQuery pour définir la sélection. C'est important! AngularJS, dans l'ombre, ajoute le préfixe à la valeur de la "valeur" attr pour les options générées par ng-repeat. Pour int c'est "nombre:".

$scope.issue.productId = productId;
function activate() {
    $http.get('/product/list')
       .then(function (response) {
           $scope.products = response.data;

           if (productId) {
               console.log("" + $("#product option").length);//for clarity
               $timeout(function () {
                   console.log("" + $("#product option").length);//for clarity
                   $('#product').val('number:'+productId);

               }, 200);
           }
       });
}
trueboroda
la source