Trabajando con select usando las opciones ng de AngularJS

442

Lo he leído en otras publicaciones, pero no pude resolverlo.

Tengo una matriz

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

Quiero renderizarlo como:

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

Y también quiero seleccionar la opción con ID = 000002.

He leído select e intentado, pero no puedo entenderlo.

Andrej Kaurin
fuente
Recomiendo encarecidamente usar Select2 , porque manejará esto por usted. Incluso hay una directiva para AngularJS .
hewstone
En realidad, existe una solución AngularJS pura desarrollada por QuantumUI . Puede encontrar más ejemplos y documentaciones en http://quantumui.org/ .
Ramon Drunce

Respuestas:

799

Una cosa a tener en cuenta es que ngModel es necesario para que ngOptions funcione ... tenga en cuenta lo ng-model="blah"que dice "establecer $ scope.blah en el valor seleccionado".

Prueba esto:

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

Aquí hay más de la documentación de AngularJS (si no la ha visto):

para fuentes de datos de matriz:

  • etiqueta de valor en la matriz
  • seleccione como etiqueta para el valor en la matriz
  • etiqueta grupo por grupo para el valor en la matriz = seleccionar como etiqueta grupo por grupo para el valor en la matriz

para fuentes de datos de objeto:

  • etiqueta para (clave, valor) en el objeto
  • seleccionar como etiqueta para (clave, valor) en el objeto
  • etiqueta grupo por grupo para (clave, valor) en el objeto
  • seleccione como etiqueta grupo por grupo para (clave, valor) en el objeto

Para algunas aclaraciones sobre los valores de etiqueta de opción en AngularJS:

Cuando lo use ng-options, los valores de las etiquetas de opción escritas por ng-options siempre serán el índice del elemento de matriz con el que se relaciona la etiqueta de opción . Esto se debe a que AngularJS realmente le permite seleccionar objetos completos con controles de selección, y no solo tipos primitivos. Por ejemplo:

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>

Lo anterior le permitirá seleccionar un objeto completo $scope.selectedItemdirectamente. El punto es que, con AngularJS, no necesita preocuparse por lo que hay en su etiqueta de opción. Deje que AngularJS se encargue de eso; solo debe preocuparse por lo que hay en su modelo en su alcance.

Aquí hay un plunker que demuestra el comportamiento anterior y muestra el HTML escrito


Tratar con la opción predeterminada:

Hay algunas cosas que no he mencionado anteriormente en relación con la opción predeterminada.

Seleccionando la primera opción y eliminando la opción vacía:

Puede hacer esto agregando un simple ng-initque establece el modelo (desde ng-model) al primer elemento en los elementos en los que repite ng-options:

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

Nota: Esto podría volverse un poco loco si foose inicializa correctamente a algo "falso". En ese caso, lo más probable es que desee manejar la inicialización de foosu controlador.

Personalizar la opción predeterminada:

Esto es un poco diferente; aquí todo lo que necesita hacer es agregar una etiqueta de opción como elemento secundario de su selección, con un atributo de valor vacío, luego personalizar su texto interno:

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

Nota: En este caso, la opción "vacía" permanecerá allí incluso después de seleccionar una opción diferente. Este no es el caso del comportamiento predeterminado de las selecciones en AngularJS.

Una opción predeterminada personalizada que se oculta después de realizar una selección:

Si desea que su opción predeterminada personalizada desaparezca después de seleccionar un valor, puede agregar un atributo ng-hide a su opción predeterminada:

<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
fuente
3
Resulta que esos son los índices de los valores, así es como angular puede permitirle usar objetos como el valor de su cuadro de selección. Angular está haciendo muchas cosas para usted detrás de escena con cuadros de selección, y se supone que no debe preocuparse por el atributo de valor en sus opciones.
Ben Lesh
1
La documentación está en "select" en su sitio: docs.angularjs.org/api/ng.directive:select
Ben Lesh
25
"... ngModel es necesario para que ngOptions funcione ..." fue el quid del problema para mí. Buena respuesta.
tristanm
1
¿Es posible evitar la primera opción vacía ( <option value="?" selected="selected"></option>)?
swenedo
44
Estoy tratando de usar el último caso ( una opción predeterminada personalizada que se oculta después de hacer una selección ) pero encontré algunos problemas. En lugar de ng-if="foo"tener que usar ng-if=!foopara ocultar la opción vacía predeterminada cuando se selecciona otra opción. Además, la opción vacía predeterminada aparece siempre en la parte inferior de la lista combinada. ¿Cómo puedo ponerlo al comienzo de la lista combinada?
Fran Herrero
90

Estoy aprendiendo AngularJS y también estaba luchando con la selección. Sé que esta pregunta ya está respondida, pero de todos modos quería compartir más código.

En mi prueba tengo dos cuadros de lista: marcas de automóviles y modelos de automóviles. La lista de modelos está deshabilitada hasta que se seleccione alguna marca. Si la selección en el cuadro de lista de marcas se restablece más tarde (se establece en 'Seleccionar marca'), el cuadro de lista de modelos se deshabilita nuevamente Y su selección también se restablece (a 'Seleccionar modelo'). Las marcas se recuperan como un recurso, mientras que los modelos solo están codificados.

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

Archivo 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
fuente
27
Estimado usuario aleatorio. Si bien esta pregunta y su respuesta son bastante simples y sin complicaciones, su organización de código en sus ubicaciones adecuadas y respectivas (controlador, servicio, plantilla, datos) muestra la elegancia de AngularJS en su forma más simple y predeterminada. Impresionante ejemplo.
Atticus
1
No es así como debe usarse. ng-modeldebe apuntar a otra variable en su alcance, no relacionada con make. Vea el ejemplo en docs.angularjs.org/api/ng/directive/ngOptions
Dmitri Zaitsev
@DmitriZaitsev Creo que te equivocas, solo porque el ejemplo de documentos angulares lo muestra como lo describiste no significa que esa sea la única manera. Muéstranos por qué crees que no se debe usar de esta manera en lugar de descartar un gran ejemplo para los novatos.
JRT
39

Por alguna razón, AngularJS me permite confundirme. Su documentación es bastante horrible sobre esto. Más buenos ejemplos de variaciones serían bienvenidos.

De todos modos, tengo una ligera variación en la respuesta de Ben Lesh.

Mis colecciones de datos se ven así:

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

Ahora

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

todavía resultó en el valor de las opciones para ser el índice (0, 1, 2, etc.).

Añadiendo Track By lo arregló para mí:

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

Creo que sucede más a menudo que desea agregar una matriz de objetos en una lista de selección, ¡así que recordaré este!

Tenga en cuenta que desde AngularJS 1.4 ya no puede usar ng-options, pero debe usarlo ng-repeaten su etiqueta de opción:

<select name="test">
   <option ng-repeat="item in items" value="{{item.key}}">{{item.value}}</option>
</select>
Mattijs
fuente
3
Esta respuesta parece ser exactamente lo que está pidiendo la pregunta. Cualquier otra respuesta le dice al lector que no se preocupe por el HTML que se genera, pero si el elemento select está en un formulario, me preocupo mucho. ¡Gran respuesta!
Keith
3
Cabe señalar que esto almacena el objeto completo seleccionado en el modelo, no solo la clave. Si solo desea la clave en el modelo, desea utilizar la versión "item.key as item.value". Esto me confundió por un tiempo, ya que estaba obsesionado con el aspecto del HTML, no con los datos que quería en el modelo, que para mí es lo importante.
mhenry1384
@ mhenry1384 Sí, tiene razón, al almacenar todo el objeto. De hecho, me gusta esa característica porque le da acceso a más que solo la ID (si la necesita). Me funciona bien cuando lleno listas con una colección de mongo y necesito alguna propiedad del elemento seleccionado.
Mattijs
2
¿Y cómo podemos detectar cambios con "ng-change" dentro option?
Konstantinos Natsios
2
Con respecto a la actualización, esto no se ve bien. ng-options todavía funciona bien en 1.5. También todavía se muestra en la documentación. docs.angularjs.org/api/ng/directive/select
Jeremy A. West
15

La pregunta ya está respondida (por cierto, una respuesta realmente buena y completa proporcionada por Ben), pero me gustaría agregar otro elemento para completar, que también puede ser muy útil.

En el ejemplo sugerido por Ben:

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

la siguientes ngOptions forma se ha utilizado: select as label for value in array.

Etiqueta es una expresión, cuyo resultado será la etiqueta del <option>elemento. En ese caso, puede realizar ciertas concatenaciones de cadenas para tener etiquetas de opciones más complejas.

Ejemplos:

  • ng-options="item.ID as item.Title + ' - ' + item.ID for item in items" te da etiquetas como Title - ID
  • ng-options="item.ID as item.Title + ' (' + item.Title.length + ')' for item in items"le da etiquetas como Title (X), donde Xes la longitud de la cadena de título.

También puede usar filtros, por ejemplo,

  • ng-options="item.ID as item.Title + ' (' + (item.Title | uppercase) + ')' for item in items"le da etiquetas como Title (TITLE), donde el valor de Título de la propiedad Título y TÍTULO es el mismo valor pero convertido a mayúsculas.
  • ng-options="item.ID as item.Title + ' (' + (item.SomeDate | date) + ')' for item in items"le da etiquetas como Title (27 Sep 2015), si su modelo tiene una propiedadSomeDate
Tom
fuente
7

En 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
fuente
1
No olvides tu radixpara parseInt: http://davidwalsh.name/parseint-radix
GFoley83
6

Si necesita un título personalizado para cada opción, ng-optionsno es aplicable. En lugar de usar ng-repeatcon opciones:

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

Espero que lo siguiente funcione para ti.

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

Puede ser útil Los enlaces no siempre funcionan.

<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 ejemplo, completa el modelo de origen de la lista de opciones de un servicio REST. Se conocía un valor seleccionado antes de completar la lista, y se configuró. Después de ejecutar la solicitud REST con $ http, la opción de lista está lista.

Pero la opción seleccionada no está configurada. Por razones desconocidas, AngularJS en la ejecución de shadow $ digest no se une seleccionado como debería ser. Tengo que usar jQuery para configurar el seleccionado. Es importante! AngularJS, en la sombra, agrega el prefijo al valor del "valor" del atributo generado por las opciones ng-repeat. Para int es "número:".

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