Lavorare con select usando le opzioni ng di AngularJS


442

Ne ho letto in altri post, ma non sono riuscito a capirlo.

Ho un array,

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

Voglio renderlo come:

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

E voglio anche selezionare l'opzione con ID = 000002.

Ho letto select e provato, ma non riesco a capirlo.


Consiglio vivamente di usare Select2 , perché lo gestirà per te. Esiste persino una direttiva per AngularJS .
Hewstone,


In realtà esiste una soluzione AngularJS pura sviluppata da QuantumUI . Puoi trovare altri esempi e documentazioni su http://quantumui.org/ .
Ramon Drunce il

Risposte:


799

Una cosa da notare è che ngModel è necessario per far funzionare ngOptions ... nota ciò ng-model="blah"che dice "imposta $ scope.blah sul valore selezionato".

Prova questo:

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

Ecco altro dalla documentazione di AngularJS (se non l'hai ancora visto):

per origini dati array:

  • etichetta per valore nella matrice
  • selezionare come etichetta per valore nella matrice
  • etichetta gruppo per gruppo per valore in matrice = seleziona come etichetta gruppo per gruppo per valore in matrice

per origini dati oggetto:

  • etichetta per (chiave, valore) nell'oggetto
  • seleziona come etichetta per (chiave, valore) nell'oggetto
  • etichetta gruppo per gruppo per (chiave, valore) nell'oggetto
  • selezionare come etichetta gruppo per gruppo per (chiave, valore) nell'oggetto

Per alcuni chiarimenti sui valori dei tag delle opzioni in AngularJS:

Quando si utilizza ng-options, i valori dei tag di opzione scritti da ng-options saranno sempre l'indice dell'elemento dell'array a cui si riferisce il tag di opzione . Questo perché AngularJS in realtà ti consente di selezionare interi oggetti con controlli di selezione e non solo tipi primitivi. Per esempio:

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>

Quanto sopra ti permetterà di selezionare direttamente un intero oggetto $scope.selectedItem. Il punto è, con AngularJS, non devi preoccuparti di cosa c'è nel tuo tag opzione. Lascia che AngularJS lo gestisca; dovresti preoccuparti solo di ciò che è nel tuo modello nel tuo ambito.

Ecco un plunker che dimostra il comportamento sopra e mostra l'HTML scritto


Trattare con l'opzione predefinita:

Ci sono alcune cose che non ho menzionato sopra relative all'opzione predefinita.

Selezione della prima opzione e rimozione dell'opzione vuota:

Puoi farlo aggiungendo un semplice ng-initche imposta il modello (da ng-model) al primo elemento negli elementi in cui si ripete ng-options:

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

Nota: questo potrebbe diventare un po 'folle se foosembra essere inizializzato correttamente in qualcosa di "falso". In tal caso, ti consigliamo di gestire l'inizializzazione foonel tuo controller, molto probabilmente.

Personalizzazione dell'opzione predefinita:

Questo è un po 'diverso; qui tutto ciò che devi fare è aggiungere un tag opzione come figlio di tua scelta, con un attributo di valore vuoto, quindi personalizzarne il testo interno:

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

Nota: in questo caso l'opzione "vuota" rimarrà lì anche dopo aver selezionato un'opzione diversa. Questo non è il caso del comportamento predefinito di selezioni in AngularJS.

Un'opzione predefinita personalizzata che si nasconde dopo aver effettuato una selezione:

Se desideri che l'opzione predefinita personalizzata scompaia dopo aver selezionato un valore, puoi aggiungere un attributo ng-hide all'opzione predefinita:

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

3
Si scopre che quelli sono gli indici dei valori, ecco come angolare può permetterti di usare gli oggetti come valore della tua casella di selezione. Angular sta facendo un sacco di cose per te dietro le quinte con le caselle selezionate e non dovresti preoccuparti dell'attributo value sulle tue opzioni.
Ben Lesh,

1
La documentazione è sotto "seleziona" sul loro sito: docs.angularjs.org/api/ng.directive:select
Ben Lesh,

25
"... ngModel è necessario per far funzionare ngOptions ..." è stato il punto cruciale del problema per me. Buona risposta.
tristanm,

1
È possibile evitare la prima opzione vuota ( <option value="?" selected="selected"></option>)?
swenedo,

4
Sto cercando di utilizzare l'ultimo caso ( un'opzione predefinita personalizzata che si nasconde dopo aver effettuato una selezione ) ma ho riscontrato alcuni problemi. Invece ng-if="foo"che ho dovuto usare ng-if=!fooper nascondere l'opzione vuota predefinita quando è selezionata un'altra opzione. Inoltre, l'opzione vuota predefinita appare sempre in fondo all'elenco combo. Come posso metterlo all'inizio dell'elenco combo?
Fran Herrero,

90

Sto imparando AngularJS e stavo lottando anche con la selezione. So che a questa domanda è già stata data una risposta, ma volevo comunque condividere altro codice.

Nel mio test ho due caselle di riepilogo: marche di auto e modelli di auto. L'elenco dei modelli è disabilitato fino a quando non viene selezionata una marca. Se la selezione nella casella di riepilogo viene successivamente ripristinata (impostata su "Seleziona marca"), la casella di riepilogo dei modelli viene nuovamente disabilitata E anche la sua selezione viene ripristinata (su "Seleziona modello"). I marchi vengono recuperati come risorsa mentre i modelli sono semplicemente codificati.

Rende 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}
    });
});

File 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"};
}

27
Gentile utente casuale. Mentre questa domanda e la sua risposta sono piuttosto semplici e senza complicazioni, l'organizzazione del codice nelle posizioni appropriate e rispettive (controller, servizio, modello, dati) mostra l'eleganza di AngularJS nella sua forma più semplice e predefinita. Fantastico esempio
Atticus,

1
Non è così che deve essere utilizzato. ng-modeldovrebbe puntare a un'altra variabile nel proprio ambito, non correlata a make. Vedi l'esempio in docs.angularjs.org/api/ng/directive/ngOptions
Dmitri Zaitsev

@DmitriZaitsev Penso che ti sbagli, solo perché l'esempio dei documenti angolari lo mostra nel modo in cui hai descritto non significa che sia l'unico modo. Mostraci perché pensi che non dovrebbe essere usato in questo modo piuttosto che spazzare un ottimo esempio per i neofiti.
JRT

39

Per qualche ragione AngularJS mi permette di confondermi. La loro documentazione è piuttosto orribile su questo. Altri buoni esempi di variazioni sarebbero i benvenuti.

Comunque, ho una leggera variazione sulla risposta di Ben Lesh.

Le mie raccolte di dati si presentano così:

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

Adesso

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

ha comunque portato il valore delle opzioni a essere l'indice (0, 1, 2, ecc.).

Aggiunta di traccia risolto per me:

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

Suppongo che accada più spesso che tu voglia aggiungere una matrice di oggetti in un elenco selezionato, quindi me lo ricorderò!

Tenere presente che da AngularJS 1.4 non è più possibile utilizzare ng-options, ma è necessario utilizzare ng-repeatil tag opzione:

<select name="test">
   <option ng-repeat="item in items" value="{{item.key}}">{{item.value}}</option>
</select>

3
Questa risposta sembra essere esattamente ciò che la domanda sta ponendo. Ogni altra risposta dice al lettore di non preoccuparsi di ciò che viene generato HTML, ma se l'elemento select è in una forma, mi preoccupo molto. Bella risposta!
Keith,

3
Va sottolineato che questo memorizza l'intero oggetto selezionato nel modello, non solo la chiave. Se si desidera solo la chiave nel modello, si desidera utilizzare la versione "item.key as item.value". Questo mi ha confuso per un po ', dato che ero appeso a come appariva l'HTML, non ai dati che volevo nel modello, che è, per me, la cosa importante.
mhenry1384,

@ mhenry1384 Sì, hai ragione, a proposito della memorizzazione dell'intero oggetto. In realtà mi piace quella funzione perché ti dà accesso a più di un semplice ID (se ne hai bisogno). Funziona bene per me quando popolare elenchi con la raccolta di mongo e ho bisogno di alcune proprietà dall'elemento selezionato.
Mattijs,

2
E come possiamo rilevare i cambiamenti con "ng-change" all'interno option?
Konstantinos Natsios,

2
Per quanto riguarda l'aggiornamento, questo non sembra corretto. ng-options funziona ancora bene in 1.5. È anche ancora mostrato nella documentazione. docs.angularjs.org/api/ng/directive/select
Jeremy A. West

15

La domanda ha già una risposta (a proposito, risposta davvero buona e completa fornita da Ben), ma vorrei aggiungere un altro elemento per completezza, che può anche essere molto utile.

Nell'esempio suggerito da Ben:

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

è stato utilizzato il seguente modulo ngOptions :select as label for value in array .

Etichetta è un'espressione, il cui risultato sarà l'etichetta per l' <option>elemento. In tal caso è possibile eseguire determinate concatenazioni di stringhe, al fine di avere etichette di opzioni più complesse.

Esempi:

  • ng-options="item.ID as item.Title + ' - ' + item.ID for item in items" ti dà etichette come Title - ID
  • ng-options="item.ID as item.Title + ' (' + item.Title.length + ')' for item in items"ti dà etichette come Title (X), dove Xè la lunghezza della stringa del titolo.

Puoi anche utilizzare i filtri, ad esempio,

  • ng-options="item.ID as item.Title + ' (' + (item.Title | uppercase) + ')' for item in items"ti dà etichette come Title (TITLE), dove il valore Titolo della proprietà Titolo e TITOLO è lo stesso valore ma convertito in caratteri maiuscoli.
  • ng-options="item.ID as item.Title + ' (' + (item.SomeDate | date) + ')' for item in items"ti dà etichette come Title (27 Sep 2015), se il tuo modello ha una proprietàSomeDate

7

In 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>

1
Non dimenticare il tuo radixper parseInt: http://davidwalsh.name/parseint-radix
GFoley83

6

Se è necessario un titolo personalizzato per ciascuna opzione, ng-optionsnon è applicabile. Invece usa ng-repeatcon le opzioni:

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

3

Spero che quanto segue funzionerà per te.

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

1

Può essere utile I binding non funzionano sempre.

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

Ad esempio, si riempie il modello di origine dell'elenco di opzioni da un servizio REST. Un valore selezionato era noto prima di compilare l'elenco ed è stato impostato. Dopo aver eseguito la richiesta REST con $ http, l'opzione dell'elenco è terminata.

Ma l'opzione selezionata non è impostata. Per ragioni sconosciute AngularJS nell'esecuzione di $ shadow digest non associa la selezione come dovrebbe essere. Devo usare jQuery per impostare il selezionato. È importante! AngularJS, in ombra, aggiunge il prefisso al valore del "valore" attr generato da opzioni ng-repeat. Per int è "numero:".

$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);
           }
       });
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.