Angularjs ng-options usando el número para el modelo no selecciona el valor inicial


Estoy tratando de usar ng-options con un <select> para enlazar un valor entero numérico a una lista de opciones correspondientes. En mi controlador, tengo algo como esto:

myApp.controller('MyCtrl', function() {
    var self = this;

    var unitOptionsFromServer = {
        2: "mA",
        3: "A",
        4: "mV",
        5: "V",
        6: "W",
        7: "kW"
    };

    self.unitsOptions = Object.keys(unitOptionsFromServer).map(function (key) {
        return { id: key, text: unitOptionsFromServer[key] };
    });

    self.selectedUnitOrdinal = 4; // Assume this value came from the server.
});

HTML:

<div ng-controller="MyCtrl as ctrl">
    <div>selectedUnitOrdinal: {{ctrl.selectedUnitOrdinal}}</div>
    <select ng-model="ctrl.selectedUnitOrdinal" ng-options="unit.id as unit.text for unit in ctrl.unitsOptions"></select>
</div>

Y aquí está un jsFiddle demostrando el problema, y algunos otros enfoques que he tomado, pero no estoy contento con.

La opción select se inicializa con un valor vacío, en lugar de "mV" como se espera en este ejemplo. La encuadernación parece funcionar bien si selecciona una opción diferente sel selectedUnitOrdinal actualiza correctamente.

He notado que si establece el valor inicial del modelo en una cadena en lugar de un número, entonces la selección inicial funciona (ver #3 en el violín).

Realmente me gustaría que ng-options jugara bien con los valores numéricos de las opciones. ¿Cómo puedo lograr esto con elegancia?

Author: BrandonLWhite, 2015-01-23

9 answers

La documentación de

Angular para la directiva ng-select explica cómo resolver este problema. Véase https://code.angularjs.org/1.4.7/docs/api/ng/directive/select (última sección).

Puede crear una directiva convert-to-number y aplicarla a su etiqueta select:

JS :

module.directive('convertToNumber', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ngModel) {
      ngModel.$parsers.push(function(val) {
        return val != null ? parseInt(val, 10) : null;
      });
      ngModel.$formatters.push(function(val) {
        return val != null ? '' + val : null;
      });
    }
  };
});

HTML :

<select ng-model="model.id" convert-to-number>
  <option value="0">Zero</option>
  <option value="1">One</option>
  <option value="2">Two</option>
</select>

Nota: Encontré que la directiva del doc no maneja nulos, así que tuve que modificarla un poco.

 88
Author: Christophe L,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-25 22:01:14

Tal vez es un poco desordenado, pero el resultado se puede lograr sin funciones especiales justo en ng-options

<select ng-model="ctrl.selectedUnitOrdinal" ng-options="+(unit.id) as unit.text for unit in ctrl.unitsOptions"></select>
 36
Author: re-gor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-29 05:57:24

Es porque cuando se obtiene el unit.id devuelve una cadena, no un entero. Los objetos en javascript almacenan sus claves como cadenas. Así que la única manera de hacerlo es su enfoque de rodear 4 por citas.

Editar

<select ng-model="ctrl.selectedUnitOrdinal" ng-options="convertToInt(unit.id) as unit.text for unit in ctrl.unitsOptions"></select>

$scope.convertToInt = function(id){
    return parseInt(id, 10);
};
 15
Author: Mathew Berg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-23 17:18:52

También puede definir filter number, este filtro analizará automáticamente el valor de la cadena a int:

<select ng-model="ctrl.selectedUnitOrdinal" ng-options="unit.id|number as unit.text for unit in ctrl.unitsOptions"></select>


angular.module('filters').filter('number', [function() {
        return function(input) {
            return parseInt(input, 10);
        };
    }]);
 9
Author: zooblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-04 14:08:40

Solo para agregar la respuesta de Christophe: la forma más fácil de lograrlo y mantenerla es hacer una directiva:

JS:

.directive('convertToNumber', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ngModel) {
      ngModel.$parsers.push(function(val) {
        //saves integer to model null as null
        return val == null ? null : parseInt(val, 10);
      });
      ngModel.$formatters.push(function(val) {
        //return string for formatter and null as null
        return val == null ? null : '' + val ;
      });
    }
  };
});

La respuesta de Christophe no funcionará correctamente para ' 0 'ya que devuelve false en" val?" prueba.

 8
Author: s3r3k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-11 11:49:19

Angular está viendo su propiedad id como una cadena, agregue comillas:

self.selectedUnitOrdinal = "4";
 3
Author: tymeJV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-23 17:04:35

Muy similar a la respuesta de tymeJV, una solución simple es convertir el número de selección predeterminado en una cadena como esta:

self.selectedUnitOrdinal = valueThatCameFromServer.toString();

De esta manera, no tiene que codificar el número, solo puede convertir cualquier valor recibido. Es una solución fácil sin filtros ni directivas.

 2
Author: emil.c,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-04 08:13:06

Mientras la solución proporcionada por Mathew Berg funciona, probablemente romperá otras select que usan opciones de selección con valor sting. Esto puede ser un problema si intentas escribir una directiva universal para manejar selects (como hice yo).

Una buena solución es comprobar si el valor es un número (incluso si es una cadena que contiene un número) y solo hacer la conversión, así:

angular.module('<module name>').filter('intToString', [function() {
        return function(key) {
            return (!isNaN(key)) ? parseInt(key) : key;
        };
    }]);

O como método controlador:

$scope.intToString = function(key) {
    return (!isNaN(key)) ? parseInt(key) : key;
};
 0
Author: bwitkowicz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-10 14:56:48
<select ng-model="ctrl.selectedUnitOrdinal" ng-options="+(unit.id) as unit.text for unit in ctrl.unitsOptions"></select>
 0
Author: mangesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-27 13:36:41