Trabalhando com select usando as opções ng do AngularJS

442

Eu li sobre isso em outros posts, mas não consegui descobrir.

Eu tenho uma matriz

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

Eu quero renderizá-lo como:

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

E também quero selecionar a opção com o ID = 000002.

Eu li selecionar e tentei, mas não consigo descobrir.

Andrej Kaurin
fonte
Eu recomendo usar o Select2 , porque ele cuidará disso para você. Existe até uma diretiva para o AngularJS .
hewstone
Na verdade, existe uma solução AngularJS pura desenvolvida pela QuantumUI . Você pode encontrar mais exemplos e documentações em http://quantumui.org/ .
Ramon Drunče

Respostas:

799

Uma coisa a observar é que o ngModel é necessário para o ngOptions funcionar ... observe o ng-model="blah"que está dizendo "defina $ scope.blah com o valor selecionado".

Tente o seguinte:

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

Aqui está mais da documentação do AngularJS (se você ainda não o viu):

para fontes de dados de matriz:

  • rótulo para valor na matriz
  • selecione como rótulo para o valor na matriz
  • grupo de etiquetas por grupo para o valor na matriz = selecione como grupo de etiquetas por grupo para o valor na matriz

para fontes de dados do objeto:

  • rótulo para (chave, valor) no objeto
  • selecione como rótulo para (chave, valor) no objeto
  • grupo de etiquetas por grupo para (chave, valor) no objeto
  • selecione como grupo de etiquetas por grupo para (chave, valor) no objeto

Para algum esclarecimento sobre os valores das tags de opção no AngularJS:

Quando você usa ng-options, os valores das tags de opção gravados por ng-options sempre serão o índice do item da matriz à qual a tag de opção se refere . Isso ocorre porque o AngularJS realmente permite selecionar objetos inteiros com controles selecionados, e não apenas tipos primitivos. Por exemplo:

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>

A descrição acima permitirá que você selecione um objeto inteiro $scope.selectedItemdiretamente. O ponto é que, com o AngularJS, você não precisa se preocupar com o que está em sua tag de opção. Deixe o AngularJS lidar com isso; você só deve se preocupar com o que está no seu modelo no seu escopo.

Aqui está um plunker demonstrando o comportamento acima e mostrando o HTML gravado


Lidando com a opção padrão:

Há algumas coisas que não mencionei acima relacionadas à opção padrão.

Selecionando a primeira opção e removendo a opção vazia:

Você pode fazer isso adicionando um simples ng-initque define o modelo (de ng-model) para o primeiro elemento nos itens em que está repetindo ng-options:

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

Nota: Isso pode ficar um pouco louco, se foofor inicializado corretamente com algo "falso". Nesse caso, você desejará lidar com a inicialização do fooseu controlador, provavelmente.

Customizando a opção padrão:

Isto é um pouco diferente; aqui tudo o que você precisa fazer é adicionar uma tag de opção como filho do seu select, com um atributo de valor vazio, e personalizar seu texto interno:

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

Nota: Nesse caso, a opção "vazia" permanecerá lá, mesmo depois que você selecionar uma opção diferente. Este não é o caso do comportamento padrão de seleções no AngularJS.

Uma opção padrão personalizada que oculta após a seleção ser feita:

Se você deseja que sua opção padrão personalizada desapareça depois de selecionar um valor, você pode adicionar um atributo ng-hide à sua opção padrão:

<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
fonte
3
Acontece que esses são os índices dos valores; é assim que o angular pode permitir que você use objetos como o valor da sua caixa de seleção. A Angular está fazendo muitas coisas para você nos bastidores com caixas de seleção, e você não deveria se preocupar com o atributo value em suas opções.
Ben Lesh
1
A documentação está em "select" no site: docs.angularjs.org/api/ng.directive:select
Ben Lesh
25
"... o ngModel é necessário para que o ngOptions funcione ..." foi o ponto crucial do problema para mim. Boa resposta.
Tristanm 23/05
1
É possível evitar a primeira opção vazia ( <option value="?" selected="selected"></option>)?
swenedo
4
Estou tentando usar o último caso ( uma opção padrão personalizada que oculta após a seleção ), mas encontrei alguns problemas. Em vez de ng-if="foo"precisar usar ng-if=!foopara ocultar a opção vazia padrão quando outra opção estiver selecionada. Além disso, a opção vazia padrão aparece sempre na parte inferior da lista de combinação. Como posso colocá-lo no início da lista de combinação?
precisa saber é o seguinte
90

Estou aprendendo o AngularJS e também estava lutando com a seleção. Eu sei que essa pergunta já foi respondida, mas eu queria compartilhar mais um pouco de código.

No meu teste, tenho duas caixas de listagem: marcas de carros e modelos de carros. A lista de modelos é desativada até que alguma marca seja selecionada. Se a seleção na caixa de listagem make for redefinida posteriormente (definida como 'Select Make'), a caixa de listagem de modelos será desativada novamente E sua seleção também será redefinida (para 'Select Model'). As marcas são recuperadas como um recurso, enquanto os modelos são apenas codificados.

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

Arquivo 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
fonte
27
Caro usuário aleatório. Embora essa pergunta e sua resposta sejam bastante simples e descomplicadas, sua organização do código em seus locais apropriados e respectivos (controlador, serviço, modelo, dados) mostra a elegância do AngularJS em sua forma mais simples e padrão. Exemplo impressionante.
Atticus
1
Não é assim que deve ser usado. ng-modeldeve apontar para outra variável no seu escopo, não relacionada a make. Veja o exemplo em docs.angularjs.org/api/ng/directive/ngOptions
Dmitri Zaitsev
@DmitriZaitsev Acho que você está errado, apenas porque o exemplo de documentação angular mostra o caminho que você descreveu não significa que esse é o único caminho. Mostre-nos por que você acha que não deve ser usado dessa maneira, em vez de mostrar um ótimo exemplo para iniciantes.
JRT
39

Por alguma razão, o AngularJS me deixa confuso. A documentação deles é horrível sobre isso. Mais bons exemplos de variações seriam bem-vindos.

De qualquer forma, tenho uma pequena variação na resposta de Ben Lesh.

Minhas coleções de dados são assim:

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

Agora

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

ainda resultou no valor das opções como o índice (0, 1, 2 etc.).

Adicionando faixa Por corrigido para mim:

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

Eu acho que acontece com mais frequência que você queira adicionar uma matriz de objetos a uma lista de seleção, por isso vou lembrar dessa!

Esteja ciente de que no AngularJS 1.4 você não pode mais usar ng-options, mas precisa usar ng-repeatsua tag de opção:

<select name="test">
   <option ng-repeat="item in items" value="{{item.key}}">{{item.value}}</option>
</select>
Mattijs
fonte
3
Essa resposta parece ser exatamente o que a pergunta está pedindo. Todas as outras respostas dizem ao leitor para não se preocupar com o HTML gerado, mas se o elemento select estiver em um formulário, eu me preocupo muito. Ótima resposta!
Keith
3
Deve-se ressaltar que isso armazena o objeto selecionado completo no modelo, não apenas a chave. Se você deseja apenas a chave no modelo, deseja usar a versão "item.key como item.value". Isso me confundiu por um tempo, porque eu estava desconectado da aparência do HTML, não dos dados que eu queria no modelo, que é, para mim, o importante.
precisa saber é o seguinte
@ mhenry1384 Sim, você está certo, armazenando todo o objeto. Na verdade, eu gosto desse recurso porque ele fornece acesso a mais do que apenas o ID (se você precisar). Funciona bem para mim quando eu preencho listas com uma coleção mongo e preciso de alguma propriedade do item selecionado.
Mattijs
2
E como podemos detectar mudanças com "ng-change" por dentro option?
Konstantinos Natsios
2
Em relação à atualização, isso não parece certo. ng-options ainda funciona bem em 1.5. Também é mostrado na documentação. docs.angularjs.org/api/ng/directive/select
Jeremy A. West
15

A pergunta já está respondida (BTW, resposta realmente boa e abrangente fornecida por Ben), mas eu gostaria de acrescentar outro elemento para completar, que também pode ser muito útil.

No exemplo sugerido por Ben:

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

a seguir ngOptions forma foi usado: select as label for value in array.

Label é uma expressão cujo resultado será o rótulo do <option>elemento. Nesse caso, você pode executar certas concatenações de cadeias, para ter rótulos de opções mais complexos.

Exemplos:

  • ng-options="item.ID as item.Title + ' - ' + item.ID for item in items" fornece etiquetas como Title - ID
  • ng-options="item.ID as item.Title + ' (' + item.Title.length + ')' for item in items"fornece rótulos como Title (X), onde Xé o comprimento da cadeia de título.

Você também pode usar filtros, por exemplo,

  • ng-options="item.ID as item.Title + ' (' + (item.Title | uppercase) + ')' for item in items"fornece rótulos como Title (TITLE), onde o valor Título da propriedade Título e TITLE é o mesmo valor, mas convertido em caracteres maiúsculos.
  • ng-options="item.ID as item.Title + ' (' + (item.SomeDate | date) + ')' for item in items"fornece rótulos como Title (27 Sep 2015), se seu modelo tem uma propriedadeSomeDate
Tom
fonte
7

No 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
fonte
1
Não se esqueça a sua radixpara parseInt: http://davidwalsh.name/parseint-radix
GFoley83
6

Se você precisar de um título personalizado para cada opção, ng-optionsnão é aplicável. Em vez disso, use ng-repeatcom as opções:

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

Espero que o seguinte funcione para você.

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

Pode ser útil. As ligações nem sempre funcionam.

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

Por exemplo, você preenche o modelo de origem da lista de opções de um serviço REST. Um valor selecionado era conhecido antes de preencher a lista e foi definido. Depois de executar a solicitação REST com $ http, a opção de lista é concluída.

Mas a opção selecionada não está definida. Por razões desconhecidas, o AngularJS na execução de shadow $ digest não liga selecionado como deveria. Eu tenho que usar o jQuery para definir o selecionado. É importante! AngularJS, na sombra, adiciona o prefixo ao valor do "valor" attr para gerado pelas opções ng-repeat. Para int, é "number:".

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