Praca z select przy użyciu opcji ng AngularJS

442

Czytałem o tym w innych postach, ale nie mogłem tego rozgryźć.

Mam tablicę,

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

Chcę to wyrenderować jako:

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

A także chcę wybrać opcję o ID = 000002.

Czytałem select i próbował, ale nie mogę zrozumieć.

Andrej Kaurin
źródło
Bardzo polecam korzystanie z Select2 , ponieważ poradzi sobie z tym za Ciebie. Istnieje nawet dyrektywa dla AngularJS .
hewstone
W rzeczywistości istnieje rozwiązanie AngularJS opracowane przez QuantumUI . Więcej przykładów i dokumentacji można znaleźć na stronie http://quantumui.org/ .
Ramon Drunce,

Odpowiedzi:

799

Należy zauważyć, że ngModel jest wymagany do działania ngOptions ... zwróć uwagę nang-model="blah" to, że „ustaw $ scope.blah na wybraną wartość”.

Spróbuj tego:

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

Oto więcej z dokumentacji AngularJS (jeśli jeszcze go nie widziałeś):

dla źródeł danych tablicowych:

  • etykieta wartości w tablicy
  • wybierz jako etykietę wartości w tablicy
  • etykieta grupa po grupie dla wartości w tablicy = wybierz jako etykieta grupa po grupie dla wartości w tablicy

dla źródeł danych obiektowych:

  • etykieta dla (klucz, wartość) w obiekcie
  • wybierz jako etykietę dla (klucz, wartość) w obiekcie
  • etykieta grupa po grupie dla (klucz, wartość) w obiekcie
  • wybierz jako grupę etykiet po grupie dla (klucz, wartość) w obiekcie

W celu wyjaśnienia wartości tagów opcji w AngularJS:

Gdy używasz ng-options, wartości tagów opcji wypisanych przez ng-options zawsze będą indeksem elementu tablicy, do którego odnosi się tag opcji . Wynika to z faktu, że AngularJS pozwala na zaznaczanie całych obiektów za pomocą kontrolek wyboru, a nie tylko typów pierwotnych. Na przykład:

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>

Powyższe pozwoli ci wybrać $scope.selectedItembezpośrednio cały obiekt . Chodzi o to, że dzięki AngularJS nie musisz się martwić o to, co jest w tagu opcji. Niech AngularJS sobie z tym poradzi; powinieneś dbać tylko o to, co jest w twoim modelu w twoim zakresie.

Oto plunker demonstrujący powyższe zachowanie i pokazujący napisany HTML


Postępowanie z opcją domyślną:

Jest kilka rzeczy, o których nie wspomniałem powyżej, odnoszących się do opcji domyślnej.

Wybieranie pierwszej opcji i usuwanie pustej opcji:

Możesz to zrobić, dodając prosty element, ng-initktóry ustawia model (od ng-model) do pierwszego elementu w elementach, w których się powtarzasz ng-options:

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

Uwaga: Może to być trochę szalone, jeśli foozdarzy się, że zostanie poprawnie zainicjowane na coś „falsy”. W takim przypadku będziesz chciał obsłużyć inicjalizacjęfoo najprawdopodobniej w kontrolerze.

Dostosowywanie opcji domyślnej:

To jest trochę inne; tutaj wszystko, co musisz zrobić, to dodać tag opcji jako wybrany element podrzędny z pustym atrybutem wartości, a następnie dostosować jego tekst wewnętrzny:

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

Uwaga: w tym przypadku opcja „pusta” pozostanie tam nawet po wybraniu innej opcji. Nie dotyczy to domyślnego zachowania selekcji w AngularJS.

Dostosowana domyślna opcja, która ukrywa się po dokonaniu wyboru:

Jeśli chcesz, aby niestandardowa opcja domyślna zniknęła po wybraniu wartości, możesz dodać atrybut ng-hide do opcji domyślnej:

<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
źródło
3
Okazuje się, że są to wskaźniki wartości, w ten sposób kąt pozwala na użycie obiektów jako wartości twojego pola wyboru. Angular robi dla ciebie wiele rzeczy za kulisami z wybranymi polami i nie powinieneś się martwić atrybutem wartości opcji.
Ben Lesh,
1
Dokumentacja znajduje się pod „select” na swojej stronie: docs.quarejs.org/api/ng.directive:select
Ben Lesh
25
„... ngModel jest wymagany do działania ngOptions ...” był dla mnie sednem problemu. Dobra odpowiedź.
tristanm
1
Czy można uniknąć pierwszej pustej opcji ( <option value="?" selected="selected"></option>)?
swenedo
4
Próbuję użyć ostatniego przypadku ( niestandardowa domyślna opcja, która ukrywa się po dokonaniu wyboru ), ale znalazłem pewne problemy. Zamiast ng-if="foo"musiałem użyć, ng-if=!fooaby ukryć domyślną pustą opcję, gdy wybrana jest inna opcja. Ponadto domyślna pusta opcja pojawia się zawsze na dole listy kombinacji. Jak mogę umieścić to na początku listy kombinacji?
Fran Herrero,
90

Uczę się AngularJS i również zmagałem się z wyborem. Wiem, że na to pytanie już udzielono odpowiedzi, ale mimo to chciałem udostępnić trochę więcej kodu.

W moim teście mam dwie listy: marki samochodów i modele samochodów. Lista modeli jest wyłączona, dopóki nie zostanie wybrana marka. Jeśli wybór w powoduje, że pole listy zostanie później zresetowane (ustawione na „Wybierz markę”), wówczas pole listy modeli zostanie ponownie wyłączone ORAZ jego wybór również zostanie zresetowany (do „Wybierz model”). Sprawia, że ​​są pobierane jako zasób, podczas gdy modele są po prostu zakodowane na stałe.

Sprawia, że ​​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}
    });
});

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

kontrolery.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
źródło
27
Drogi losowy użytkowniku. Chociaż to pytanie i jego odpowiedź są raczej proste i nieskomplikowane, twoja organizacja kodu w odpowiednie i odpowiednie lokalizacje (kontroler, usługa, szablon, dane) pokazuje elegancję AngularJS w jego najprostszej i domyślnej formie. Świetny przykład.
Atticus
1
Nie tak powinno się go używać. ng-modelpowinien wskazywać inną zmienną w twoim zakresie, niezwiązaną z make. Zobacz przykład w docs.quarejs.org/api/ng/directive/ngOptions
Dmitri Zaitsev
@DmitriZaitsev Myślę, że się mylisz, tylko dlatego, że przykład kątowego dokumentu pokazuje to tak, jak opisałeś, nie oznacza, że ​​to jedyny sposób. Pokaż nam, dlaczego uważasz, że nie powinno się go używać w ten sposób, zamiast marnować świetny przykład dla początkujących.
JRT
39

Z jakiegoś powodu AngularJS pozwala mi się pomylić. Ich dokumentacja jest w tym dość okropna. Przydałoby się więcej dobrych przykładów odmian.

W każdym razie mam niewielkie różnice w odpowiedzi Bena Lesha.

Moje kolekcje danych wyglądają następująco:

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

Teraz

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

nadal powodowało, że wartością opcji był indeks (0, 1, 2 itd.).

Dodanie utworu Naprawiono to dla mnie:

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

Wydaje mi się, że częściej zdarza się, że chcesz dodać tablicę obiektów do listy wyboru, więc zapamiętam ten!

Pamiętaj, że z AngularJS 1.4 nie możesz już używać opcji ng, ale musisz użyć ng-repeattagu opcji:

<select name="test">
   <option ng-repeat="item in items" value="{{item.key}}">{{item.value}}</option>
</select>
Mattijs
źródło
3
Ta odpowiedź wydaje się być dokładnie tym, o co pyta pytanie. Każda inna odpowiedź mówi czytelnikowi, aby nie martwił się o generowany HTML, ale jeśli element select jest w formie, bardzo się martwię. Świetna odpowiedź!
Keith
3
Należy zauważyć, że przechowuje to w pełni wybrany obiekt w modelu, a nie tylko klucz. Jeśli chcesz tylko klucz w modelu, chcesz użyć wersji „item.key jako item.value”. Przez pewien czas byłem tym zdezorientowany, ponieważ odłożyłem słuchawkę, jak wyglądał HTML, a nie dane, które chciałem w modelu, co jest dla mnie najważniejsze.
mhenry1384
@ mhenry1384 Tak, masz rację, jeśli chodzi o przechowywanie całego obiektu. Naprawdę podoba mi się ta funkcja, ponieważ daje dostęp do więcej niż tylko identyfikatora (jeśli jest to potrzebne). Działa dla mnie dobrze, gdy zapełniam listy kolekcją mongo i potrzebuję trochę właściwości z wybranego elementu.
Mattijs
2
I jak możemy wykryć zmiany z „ng-zmianą” w środku option?
Konstantinos Natsios
2
Jeśli chodzi o aktualizację, nie wygląda to dobrze. Opcje ng nadal działają poprawnie w wersji 1.5. Jest to nadal widoczne w dokumentacji. docs.quarejs.org/api/ng/directive/select
Jeremy A. West
15

Odpowiedź na pytanie jest już udzielona (BTW, naprawdę dobra i wyczerpująca odpowiedź udzielona przez Bena), ale chciałbym dodać kolejny element kompletności, który może być również bardzo przydatny.

W przykładzie zasugerowanym przez Bena:

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

wymienionym poniżej ngOptions forma została wykorzystana: select as label for value in array.

Etykieta jest wyrażeniem, którego wynikiem będzie etykieta<option> elementu. W takim przypadku możesz wykonać określone konkatenacje ciągów, aby uzyskać bardziej złożone etykiety opcji.

Przykłady:

  • ng-options="item.ID as item.Title + ' - ' + item.ID for item in items" daje etykiety jak Title - ID
  • ng-options="item.ID as item.Title + ' (' + item.Title.length + ')' for item in items"daje etykiety takie jak Title (X), gdzie Xjest długość ciągu tytułu.

Możesz także użyć filtrów, na przykład

  • ng-options="item.ID as item.Title + ' (' + (item.Title | uppercase) + ')' for item in items" daje etykiety jak Title (TITLE) , gdzie wartość tytułu właściwości tytułu i TYTUŁU jest taka sama, ale jest konwertowana na wielkie litery.
  • ng-options="item.ID as item.Title + ' (' + (item.SomeDate | date) + ')' for item in items"daje etykiety, np. Title (27 Sep 2015)jeśli twój model ma właściwośćSomeDate
Tomek
źródło
7

W 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
źródło
1
Nie zapomnij radixo parseInt: http://davidwalsh.name/parseint-radix
GFoley83
6

Jeśli potrzebujesz niestandardowego tytułu dla każdej opcji, ng-optionsnie dotyczy. Zamiast tego użyj ng-repeatz opcjami:

<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
źródło
3

Mam nadzieję, że następujące działania będą dla Ciebie działać.

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

To może być przydatne. Wiązania nie zawsze działają.

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

Na przykład wypełniasz model źródłowy listy opcji z usługi REST. Wybrana wartość była znana przed wypełnieniem listy i została ustawiona. Po wykonaniu żądania REST za pomocą $ http opcja listy jest wykonywana.

Ale wybrana opcja nie jest ustawiona. Z nieznanych powodów wykonanie AngularJS w cieniu $ digest nie wiąże wybranego tak, jak powinno. Muszę użyć jQuery, aby ustawić wybrane. To ważne! AngularJS, w cieniu, dodaje przedrostek do wartości „wartości” attr dla opcji generowanych przez ng-repeat. Dla int jest to „liczba:”.

$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
źródło