63

I'm trying to use ng-options with a <select> to bind a numeric integer value to a list of corresponding options. In my controller, I have something like this:

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>

And here's a jsFiddle demonstrating the problem, and some other approaches I've taken but am not happy with.

The select option is being initialized with an empty value, instead of "mV" as expected in this example. The binding seems to work fine if you select a different option -- selectedUnitOrdinal updates properly.

I've noticed that if you set the initial model value to a string instead of a number, then the initial selection works (see #3 in the fiddle).

I really would like ng-options to play nice with numeric option values. How can I achieve this elegantly?

4

12 回答 12

107

Angular 的ng-select指令文档解释了如何解决这个问题。请参阅https://code.angularjs.org/1.4.7/docs/api/ng/directive/select(最后一节)。

您可以创建一个convert-to-number指令并将其应用于您的选择标签:

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>

注意:我发现文档中的指令不处理空值,所以我不得不稍微调整一下。

于 2015-12-06T05:01:24.463 回答
46

也许它有点乱,但结果可以在 ng-options 中没有特殊功能的情况下实现

<select ng-model="ctrl.selectedUnitOrdinal" ng-options="+(unit.id) as unit.text for unit in ctrl.unitsOptions"></select>
于 2015-12-29T05:57:24.670 回答
17

这是因为当您获得 unit.id 时,它返回的是字符串而不是整数。javascript 中的对象将它们的键存储为字符串。所以唯一的方法是你用引号包围 4 的方法。

编辑

<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);
};
于 2015-01-23T17:04:20.913 回答
9

您还可以定义 filter number,此过滤器会自动将字符串值解析为 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);
        };
    }]);
于 2015-07-26T08:22:38.627 回答
9

只是添加克里斯托夫的回答:实现和维护它的最简单方法是制定指令:

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

Christophe 的回答对于“0”将无法正常工作,因为它在“val?”上返回 false?测试。

于 2015-12-11T09:40:48.863 回答
7

您可以使用 ng-value 属性。像这样:

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

于 2019-11-26T09:07:25.647 回答
6

我认为其他解决方案对于简单地将值建立为整数来说过于复杂。我会使用:

<select ng-model="model.id">
    <option ng-value="{{ 0 }}">Zero</option>
    <option ng-value="{{ 1 }}">One</option>
    <option ng-value="{{ 2 }}">Two</option>
</select>
于 2018-10-11T15:29:15.647 回答
3

Angular 将您的id属性视为字符串,请添加引号:

self.selectedUnitOrdinal = "4";
于 2015-01-23T17:04:35.203 回答
3

与 tymeJV 的答案非常相似,简单的解决方法是将默认选择编号转换为如下字符串:

self.selectedUnitOrdinal = valueThatCameFromServer.toString();

这样,您不必对数字进行硬编码,您只需转换任何接收到的值。这是一个简单的修复,没有任何过滤器或指令。

于 2016-08-04T08:13:06.167 回答
2
<select ng-model="ctrl.selectedUnitOrdinal" ng-options="+(unit.id) as unit.text for unit in ctrl.unitsOptions"></select>
于 2017-12-27T13:36:41.333 回答
1

将unitsOptions中的id属性设为数字

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

http://jsfiddle.net/htwc10nx/

于 2019-05-15T05:39:56.973 回答
0

虽然 Mathew Berg 提供的解决方案有效,但它可能会破坏其他使用 sting-valued 选择选项的选择。如果您尝试编写一个通用指令来处理选择(就像我所做的那样),这可能是一个问题。

一个很好的解决方法是检查该值是否是一个数字(即使它是一个包含数字的字符串)并且只进行转换,如下所示:

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

或作为控制器方法:

$scope.intToString = function(key) {
    return (!isNaN(key)) ? parseInt(key) : key;
};
于 2015-12-10T14:56:48.363 回答