web-dev-qa-db-fra.com

Angularjs ng-options utilisant number pour le modèle ne sélectionne pas la valeur initiale

J'essaie d'utiliser ng-options avec un <select> pour lier un entier numérique à une liste d'options correspondantes. Dans mon contrôleur, j'ai quelque chose comme ça:

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>

Et voici un jsFiddle démontrant le problème et quelques autres approches que j’ai prises mais qui ne me satisfont pas.

L'option select est en cours d'initialisation avec une valeur vide, au lieu de "mV" comme prévu dans cet exemple. La liaison semble fonctionner correctement si vous sélectionnez une option différente - selectedUnitOrdinal est mis à jour correctement.

J'ai remarqué que si vous définissez la valeur initiale du modèle sur une chaîne au lieu d'un nombre, la sélection initiale fonctionne (voir n ° 3 du violon).

Je voudrais vraiment que ng-options joue Nice avec des valeurs d’options numériques. Comment puis-je réaliser cela avec élégance?

57
BrandonLWhite

La documentation d'Angular sur la directive ng-select explique comment résoudre ce problème. Voir https://code.angularjs.org/1.4.7/docs/api/ng/directive/select (dernière section).

Vous pouvez créer une directive convert-to-number et l'appliquer à votre balise 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>

Note: J'ai trouvé que la directive de la doc ne gère pas les NULL, j'ai donc dû la modifier un peu.

96
Christophe L

C'est peut-être un peu brouillon, mais le résultat peut être obtenu sans fonctions spéciales directement dans les options

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

C'est parce que lorsque vous obtenez unit.id, il retourne une chaîne et non un entier. Les objets en javascript stockent leurs clés sous forme de chaînes. Donc, la seule façon de le faire est votre approche consistant à entourer 4 de citations.

Modifier

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

Vous pouvez également définir le filtre number, ce filtre analysera automatiquement la valeur de chaîne dans 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
zooblin

Juste pour ajouter la réponse de Christophe: le moyen le plus simple d'y parvenir et de le maintenir consiste à faire une directive:

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 tenue de jeu de Christophe ne fonctionnera pas correctement pour "0" car elle renvoie false sur "val?" tester.

8
s3r3k

Angular voit votre propriété id comme une chaîne, ajoutez des guillemets:

self.selectedUnitOrdinal = "4";
3
tymeJV

De manière très similaire à la réponse de tymeJV, une solution simple consiste à convertir le numéro de sélection par défaut en une chaîne comme celle-ci:

self.selectedUnitOrdinal = valueThatCameFromServer.toString();

De cette façon, vous n'avez pas à coder en dur le numéro, vous pouvez simplement convertir n'importe quelle valeur reçue. C'est une solution facile sans filtres ni directives.

3
emil.c

Je pense que les autres solutions sont trop complexes pour établir simplement la valeur sous forme d’entier. J'utiliserais:

<select ng-model="model.id">
    <option ng-value="{{ 0 }}">Zero</option>
    <option ng-value="{{ 1 }}">One</option>
    <option ng-value="{{ 2 }}">Two</option>
</select>
2
Ted Scheckler

Définir la propriété id dans unitsOptions en tant que nombre

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

http://jsfiddle.net/htwc10nx/

1
oadcub
<select ng-model="ctrl.selectedUnitOrdinal" ng-options="+(unit.id) as unit.text for unit in ctrl.unitsOptions"></select>
0
mangesh

Bien que la solution fournie par Mathew Berg fonctionne, elle cassera probablement d’autres sélections utilisant des options de sélection à la piqûre. Cela peut poser problème si vous essayez d’écrire une directive universelle pour le traitement des sélections (comme je l’ai fait). 

Une solution de contournement intéressante consiste à vérifier si la valeur est un nombre (même s’il s’agit d’une chaîne contenant un nombre) et uniquement à la conversion, comme suit:

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

ou comme méthode de contrôleur:

$scope.intToString = function(key) {
    return (!isNaN(key)) ? parseInt(key) : key;
};
0
bwitkowicz