在angularJS select指令中设置所选项目

时间:2013-03-10 18:54:23

标签: angularjs angularjs-directive

我在使用angular的select指令设置所选项时遇到问题。我不知道这是角度设计师的错误还是有意识的设计。它确实使select指令的用处不那么有用。

说明

我的应用程序与REST API通信以从数据库接收实体。 API规定对象的关系仅与ID属性一起发送,以便您可以在后续请求中检索它们(如果需要)。

示例:

{ id : 1, customerName : "some name", city : {id : 12}} 

其中city是另一个可以使用城市ID通过不同的REST端点检索的实体,如下所示:

{ id: 12, name : "New York"}

我需要创建一个表单来编辑客户实体,其中包含所有可能城市的下拉菜单,以便用户可以从列表中选择适当的城市。该列表必须首先显示从JSON对象检索到的客户的城市。

表格如下:

 <form>
  <input type="text" ng-model="customer.name"/>
  <select ng-model="customer.city" ng-options="i as i.name for i in cities"></select>
 </form> 

控制器看起来像这样:

app.controller('MainCtrl', function ($scope, $http) {
    $http.get(serviceurl + 'admin/rest/customer/' + id + "/", {
        params: {"accept": "json"},
        withCredentials: true
    }).then(function (response) {
                $scope.customer = response.data.item;
            });
    $http.get(serviceurl + 'admin/rest/city/', {
        params: {"accept": "json"},
        withCredentials: true
    }).then(function (response) {
                $scope.cities = response.data.items;
                // THIS LINE LOADS THE ACTUAL DATA FROM JSON
                $scope.customer.city = $scope.findCity($scope.customer.city);
            });
    $scope.findCity = function (city) {
        for (var i = 0; i < $scope.cities.length; i++) {
            if ($scope.cities[i].id == city.id) {
                return $scope.cities[i];
            }
        }
    }
});

应该怎么做: 加载City对象的完整详细信息后,select指令必须将已加载的城市设置为列表中的选定项。

会发生什么: 列表显示一个空项目,如果从项目数组外的对象中选择的项目,则无法初始化所选项目。

此处问题的演示:http://plnkr.co/edit/NavukDb34mjjnQOP4HE9?p=preview

有解决方案吗?是否可以通用方式以编程方式设置所选项目,以便将AJAX调用和选择逻辑重构为可重用的基于AJAX的选择小部件?

4 个答案:

答案 0 :(得分:42)

这很简单

<select
    ng-model="item"
    ng-options="item.name for item in items track by item.name">

然后在你的控制器内:

// all items
$scope.items = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
// set the active one
$scope.item = {name: 'b'};
// or just
$scope.item = $scope.items[1]

查看http://docs.angularjs.org/api/ng.directive:select 从那里:

  

trackexpr :在处理对象数组时使用。此表达式的结果将用于标识数组中的对象。 trackexpr很可能会引用值变量(例如value.propertyName)。

其余的只是为$scope.item变量赋值,而angular将通过检查项目的name属性来确定应将哪个元素设置为活动。

答案 1 :(得分:21)

它不起作用的原因是angular要求对象引用相等。在您的情况下(plnkr中的“从对象中选择”)创建一个新对象,尽管具有相同的属性。但是,Angular无法知道两个不同的对象代表同一个对象。您至少有两种方法:

查找正确的城市对象实例

不是将$scope.customer.city设置为新对象,而是从$scope.cities数组中找到实际的城市对象。如果您使用的是UnderscoreJs,则可以执行以下操作:

$scope.customer.city = _.find($scope.cities, function (city) {
    return city.id === theCustomersCity.id;
});

绑定到城市ID而不是城市对象

另一种可能更容易的方法是更改​​ng-modelng-options指令以匹配id而不是object。请参阅工作示例here

<select ng-model="customer.cityId" ng-options="i.id as i.name for i in cities"></select>

答案 2 :(得分:2)

我遇到了同样的问题。我的选项和建模数据都来自单独的API调用。

我没有在对象键上使用ng-model添加间接层,而是编写了一个使用“代理”变量的简单指令。

<select ng-model="customer.city" ng-options="i as i.name for i in cities"></select>

变为

<select ng-model="customer_cityProxy" ng-options="i.name as i.name for i in cities"></select>

在customer.city和customer_cityProxy上使用$ watch,我得到了预期的行为。

还有一些缺点,因为只有当钥匙脱离时它才有效。

此处提供代码:https://github.com/gosusnp/options-proxy

答案 3 :(得分:0)