我在使用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的选择小部件?
答案 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-model
和ng-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,我得到了预期的行为。
还有一些缺点,因为只有当钥匙脱离时它才有效。
答案 3 :(得分:0)