20

我在 Angular 的选择指令中设置所选项目时遇到问题。我不知道这是 Angular 设计师的错误还是有意识的设计。它确实使 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

4 回答 4

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 变量(例如 value.propertyName)。

剩下的只是为变量分配一个值,角度将通过检查项目的属性$scope.item来确定应该将哪个元素设置为活动的。name

于 2014-01-25T19:04:51.160 回答
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-model​​ andng-options指令以匹配 id 而不是 object。请参阅此处的工作示例。

<select ng-model="customer.cityId" ng-options="i.id as i.name for i in cities"></select>
于 2013-03-10T20:28:45.020 回答
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

于 2013-08-06T12:18:14.037 回答
0

看看http://configit.github.io/ngyn/#select_extensions这个解决方案对我有用

于 2013-08-31T10:19:00.253 回答