I have a problem with setting the selected item in angular's select directive. I don't know if this is a bug or a conscious design from the designers of angular. It sure makes the select directive a lot less useful though.
我在使用angular的select指令设置所选项时遇到问题。我不知道这是角度设计师的错误还是有意识的设计。它肯定会使select指令变得不那么有用。
Description:
描述:
My app communicates with a REST API to receive an entity from the database. The API dictates that relations of the object are sent with an ID property only so that you can retrieve them in subsequent requests if needed.
我的应用程序与REST API通信以从数据库接收实体。 API规定对象的关系仅与ID属性一起发送,以便您可以在后续请求中检索它们(如果需要)。
Example:
例:
{ id : 1, customerName : "some name", city : {id : 12}}
where city is another entity that can be retrieved through a different REST endpoint using the city id and looks like so:
city是另一个可以使用城市ID通过不同的REST端点检索的实体,如下所示:
{ id: 12, name : "New York"}
I need to create a form to edit the customer entity with a dropdown menu with all possible cities so that the user can select the appopriate city from the list. The list must initially display the customer's city as retrieved from the JSON object.
我需要使用包含所有可能城市的下拉菜单创建一个表单来编辑客户实体,以便用户可以从列表中选择适当的城市。该列表必须首先显示从JSON对象检索到的客户的城市。
The form looks like this:
表单如下所示:
<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>
And the controller looks like this:
控制器看起来像这样:
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];
}
}
}
});
What should happen: once the full details of the City object are loaded the select directive must set the city that was loaded as the selected item in the list.
应该发生什么:一旦加载了City对象的完整细节,select指令必须将加载的城市设置为列表中的选定项目。
What happens: the list displays an empty item and there's no way to initialize the selected item if the selected item from objects outside the array of items.
会发生什么:列表显示一个空项目,如果所选项目来自项目数组之外的对象,则无法初始化所选项目。
DEMO of the issue here: http://plnkr.co/edit/NavukDb34mjjnQOP4HE9?p=preview
这里的问题演示:http://plnkr.co/edit/NavukDb34mjjnQOP4HE9?p = preview
Is there a solutions for this? Can the selected item be set programmatically in a generic way so that the AJAX calls and select logic be refactored into a reusable AJAX based select widget ?
有解决方案吗?可以通用方式以编程方式设置所选项目,以便将AJAX调用和选择逻辑重构为可重用的基于AJAX的选择小部件吗?
5 个解决方案
#1
41
It is as simple as this
就这么简单
<select
ng-model="item"
ng-options="item.name for item in items track by item.name">
Then inside you controller:
然后在你的控制器内:
// 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]
Check out the http://docs.angularjs.org/api/ng.directive:select From there:
查看http://docs.angularjs.org/api/ng.directive:select从那里:
trackexpr: Used when working with an array of objects. The result of this expression will be used to identify the objects in the array. The trackexpr will most likely refer to the value variable (e.g. value.propertyName).
trackexpr:在处理对象数组时使用。此表达式的结果将用于标识数组中的对象。 trackexpr很可能会引用值变量(例如value.propertyName)。
The rest is just assigning a value to the $scope.item
variable and angular will figure out which element should be set as active by checking the item's name
property.
其余的只是为$ scope.item变量赋值,而angular将通过检查项的name属性来确定应该将哪个元素设置为活动。
#2
21
The reason it doesn't work is that angular expects the objects references to be equal. In your case (the 'select from object' in your plnkr) creates a new object, albeit with the same properties. However, Angular can't know that two different objects represents the same object. You have at least two approaches:
它不起作用的原因是angular要求对象引用相等。在您的情况下(plnkr中的“从对象中选择”)创建一个新对象,尽管具有相同的属性。但是,Angular无法知道两个不同的对象代表同一个对象。您至少有两种方法:
Find the correct city object instance
找到正确的城市对象实例
Instead of setting $scope.customer.city
to a new object, find the actual city object from the $scope.cities
array. If you're using UnderscoreJs you could do something like:
不是将$ scope.customer.city设置为新对象,而是从$ scope.cities数组中查找实际的城市对象。如果您正在使用UnderscoreJs,您可以执行以下操作:
$scope.customer.city = _.find($scope.cities, function (city) {
return city.id === theCustomersCity.id;
});
Bind to the city id instead of the city object
绑定到城市ID而不是城市对象
Another approach, which might be easier, is to change the ng-model
and ng-options
directives to match on id instead of object. See working example here.
另一种可能更容易的方法是更改ng-model和ng-options指令以匹配id而不是object。请参阅此处的工作示例
<select ng-model="customer.cityId" ng-options="i.id as i.name for i in cities"></select>
#3
2
I came accross the same problem. My options and the modeled data both came from separate API calls.
我遇到了同样的问题。我的选项和建模数据都来自单独的API调用。
Instead of adding a layer of indirection by using ng-model on the object keys, I ended up writing a simple directive that uses a "proxy" variable.
我没有通过在对象键上使用ng-model添加间接层,而是编写了一个使用“代理”变量的简单指令。
<select ng-model="customer.city" ng-options="i as i.name for i in cities"></select>
becomes
变
<select ng-model="customer_cityProxy" ng-options="i.name as i.name for i in cities"></select>
Using a $watch on customer.city and customer_cityProxy, I get the expected behaviour.
在customer.city和customer_cityProxy上使用$ watch,我得到了预期的行为。
There are still a few drawbacks, on being that it only works if the keys are disjoints.
还有一些缺点,因为它只有在键脱离时才有效。
Code is available here: https://github.com/gosusnp/options-proxy
代码可在此处获取:https://github.com/gosusnp/options-proxy
#4
0
Take a look at http://configit.github.io/ngyn/#select_extensions this solution worked for me
看一下http://configit.github.io/ngyn/#select_extensions这个解决方案对我有用
#5
-1
http://plnkr.co/edit/Lw8uadPf4G5KYXLzeaHb shows that if you set the object reference to your scope then it works. it is just a slightly different third answer to Martins
http://plnkr.co/edit/Lw8uadPf4G5KYXLzeaHb显示如果您将对象引用设置为您的范围,那么它可以正常工作。对马丁斯来说,这只是一个稍微不同的第三个答案
#1
41
It is as simple as this
就这么简单
<select
ng-model="item"
ng-options="item.name for item in items track by item.name">
Then inside you controller:
然后在你的控制器内:
// 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]
Check out the http://docs.angularjs.org/api/ng.directive:select From there:
查看http://docs.angularjs.org/api/ng.directive:select从那里:
trackexpr: Used when working with an array of objects. The result of this expression will be used to identify the objects in the array. The trackexpr will most likely refer to the value variable (e.g. value.propertyName).
trackexpr:在处理对象数组时使用。此表达式的结果将用于标识数组中的对象。 trackexpr很可能会引用值变量(例如value.propertyName)。
The rest is just assigning a value to the $scope.item
variable and angular will figure out which element should be set as active by checking the item's name
property.
其余的只是为$ scope.item变量赋值,而angular将通过检查项的name属性来确定应该将哪个元素设置为活动。
#2
21
The reason it doesn't work is that angular expects the objects references to be equal. In your case (the 'select from object' in your plnkr) creates a new object, albeit with the same properties. However, Angular can't know that two different objects represents the same object. You have at least two approaches:
它不起作用的原因是angular要求对象引用相等。在您的情况下(plnkr中的“从对象中选择”)创建一个新对象,尽管具有相同的属性。但是,Angular无法知道两个不同的对象代表同一个对象。您至少有两种方法:
Find the correct city object instance
找到正确的城市对象实例
Instead of setting $scope.customer.city
to a new object, find the actual city object from the $scope.cities
array. If you're using UnderscoreJs you could do something like:
不是将$ scope.customer.city设置为新对象,而是从$ scope.cities数组中查找实际的城市对象。如果您正在使用UnderscoreJs,您可以执行以下操作:
$scope.customer.city = _.find($scope.cities, function (city) {
return city.id === theCustomersCity.id;
});
Bind to the city id instead of the city object
绑定到城市ID而不是城市对象
Another approach, which might be easier, is to change the ng-model
and ng-options
directives to match on id instead of object. See working example here.
另一种可能更容易的方法是更改ng-model和ng-options指令以匹配id而不是object。请参阅此处的工作示例
<select ng-model="customer.cityId" ng-options="i.id as i.name for i in cities"></select>
#3
2
I came accross the same problem. My options and the modeled data both came from separate API calls.
我遇到了同样的问题。我的选项和建模数据都来自单独的API调用。
Instead of adding a layer of indirection by using ng-model on the object keys, I ended up writing a simple directive that uses a "proxy" variable.
我没有通过在对象键上使用ng-model添加间接层,而是编写了一个使用“代理”变量的简单指令。
<select ng-model="customer.city" ng-options="i as i.name for i in cities"></select>
becomes
变
<select ng-model="customer_cityProxy" ng-options="i.name as i.name for i in cities"></select>
Using a $watch on customer.city and customer_cityProxy, I get the expected behaviour.
在customer.city和customer_cityProxy上使用$ watch,我得到了预期的行为。
There are still a few drawbacks, on being that it only works if the keys are disjoints.
还有一些缺点,因为它只有在键脱离时才有效。
Code is available here: https://github.com/gosusnp/options-proxy
代码可在此处获取:https://github.com/gosusnp/options-proxy
#4
0
Take a look at http://configit.github.io/ngyn/#select_extensions this solution worked for me
看一下http://configit.github.io/ngyn/#select_extensions这个解决方案对我有用
#5
-1
http://plnkr.co/edit/Lw8uadPf4G5KYXLzeaHb shows that if you set the object reference to your scope then it works. it is just a slightly different third answer to Martins
http://plnkr.co/edit/Lw8uadPf4G5KYXLzeaHb显示如果您将对象引用设置为您的范围,那么它可以正常工作。对马丁斯来说,这只是一个稍微不同的第三个答案