输入[无线电]与ng模型和ng值的对象相等性比较
Object equality comparison for input[radio] with ng-model and ng-value
让我首先说,这个问题与使用ng-options在<select>
标签中选择的问题非常相似。例如,使用 AngularJS 的 ng-options 进行选择。具体问题是比较对象的两个不同实例,这些实例的引用不相等,但在逻辑上表示相同的数据。
为了演示,假设我们在模型中有以下选项数组和选定的选项变量:
$scope.items = [
{ID: 1, Label: 'Foo', Extra: 17},
{ID: 2, Label: 'Bar', Extra: 18},
{ID: 3, Label: 'Baz', Extra: 19}
];
$scope.selectedItem = {ID: 1, Label: 'Foo'};
请注意,上述对象仅用于演示。我特意省略了selectedItem
上的"Extra"属性,以表明有时我的模型对象在其特定属性上有所不同。重要的是我想在 ID 属性上进行比较。我在真实对象上有一个equals()
函数,可以比较原型"类"和 ID。
然后在视图中:
<label class="radio inline" ng-repeat="item in items">
<input type="radio" ng-model="selectedItem" ng-value="item"> {{item.Label}}
</label>
现在,这里的问题是"Foo"的单选按钮不会开始选中,因为角度对对象使用引用相等性。如果我将范围的最后一行更改为下面,一切都会按预期工作。
$scope.selectedItem = items[0];
但是,我遇到的问题是,在我的应用程序中,我不是简单地在作用域中声明这两个简单的变量。相反,选项列表和绑定所选选项的数据结构都是使用 $http 从服务器查询的较大 JSON 数据集的一部分。在一般情况下,我很难将数据绑定的选定属性更改为数据查询中的等效选项。
所以,我的问题:在 ng-options 中 <select>
,angular 提供了一个track by
表达式,允许我说"object.ID"之类的话,并通知 angular 它应该通过 ID 属性将所选模型值与选项进行比较。是否有类似的东西可以用于一堆无线电输入,全部绑定到同一模型属性?理想情况下,我将能够告诉 angular 使用我自己放置在这些模型对象上的自定义 equals(( 方法,该方法同时检查对象类型和 ID。
我写了一个最简单的指令。使用一种"跟踪"来映射两个不同的对象。请参阅 http://jsfiddle.net/xWWwT/146/。
.HTML
<div ng-app="app">
<div ng-app ng-controller="ThingControl">
<ul >
<li ng-repeat="color in colors">
<input type="radio" name="color" ng-model="$parent.thing" ng-value="color" radio-track-by="name" />{{ color.name }}
</li>
</ul>
Preview: {{ thing }}
</div>
</div>
.JS
var app = angular.module('app', []);
app.controller('ThingControl', function($scope){
$scope.colors = [
{ name: "White", hex: "#ffffff"},
{ name: "Black", hex: "#000000"},
{ name: "Red", hex: "#000000"},
{ name: "Green", hex: "#000000"}
];
$scope.thing = { name: "White", hex: "#ffffff"};
});
app.directive('radioTrackBy', function(){
return {
restrict: "A",
scope: {
ngModel: "=",
ngValue: "=",
radioTrackBy: "@"
},
link: function (ng) {
if (ng.ngValue[ng.radioTrackBy] === ng.ngModel[ng.radioTrackBy]) {
ng.ngModel = ng.ngValue;
}
}
};
});
好的,所以经过进一步的审查,我决定采用一种更"混合"的方法,本质上只是用我自己的自定义指令替换ng-model指令。这与我用于基于以下答案制作"复选框列表"指令的方法非常相似:https://stackoverflow.com/a/14519881/561604。
.directive('radioOptions', function() {
// Apply this directive as an attribute to multiple radio inputs. The value of the attribute
// should be the scope variable/expression which contains the available options for the
// radio list. Typically, this will be the collection variable in an ng-repeat directive
// that templates the individual radio inputs which have radio-options applied. In addition,
// instead of the normal ng-model, use a selected-option attribute set to the same expression.
// For example, you might use radio-options like this:
// <label ... ng-repeat="item in collection">
// <input type="radio" ... ng-value="item" radio-options="collection" selected-option="myModel.myProperty">
// </label>
//
// See https://stackoverflow.com/questions/19281404/object-equality-comparison-for-inputradio-with-ng-model-and-ng-value
// for the SO question that inspired this directive.
return {
scope: {
radioOptions: '=',
selectedOption: '=',
ngValue: '='
},
link: function( scope, elem, attrs ) {
var modelChanged = function() {
if( jQuery.isArray(scope.radioOptions) ) {
jQuery.each( scope.radioOptions, function(idx, item) {
// This uses our models' custom 'equals' function for comparison, but another application could use
// ID propeties, etc.
if( typeof item.equals === 'function' && item.equals(scope.selectedOption) ) {
elem.prop( 'checked', item === scope.ngValue );
}
});
}
};
scope.$watch( 'radioOptions', modelChanged );
scope.$watch( 'selectedOption', modelChanged );
var viewChanged = function() {
var checked = elem.prop( 'checked' );
if( checked ) {
scope.selectedOption = scope.ngValue;
}
};
elem.bind( 'change', function() {
scope.$apply( viewChanged );
});
}
};
});
根据 OP 的要求,下面是一个适用于复杂对象的示例单选按钮指令。它使用下划线.js从选项中查找所选项目。它比它应该的要复杂一些,因为它还支持使用 AJAX 调用加载选项和所选值。
你为什么不像这样直接使用 ID 进行选择?
<input type="radio" ng-model="selectedItem" ng-value="item.ID"> {{item.Label}}
然后,您可以编写items[selectedItem]
而不是使用selectedItem
.
哦,在jsfiddle中处理您的问题时,我注意到了其他事情:
a.(您忘记向输入添加name
属性。
b.(永远不要在ng模型中使用没有点的东西。如果您实际尝试输出 selectedItem {{selectedItem}}
在 ng-repeat 块之外,您会注意到当您选择单选按钮时,该值不会更新。这是由于ng-repeat
创建自己的子范围。
由于我还不能添加评论,所以我必须在这里回复。达娜的回答对我来说还行。虽然我想指出为了使用他的方法,但必须在集合中的对象上实现"equals"函数。请参阅以下示例:
.controller('ExampleController', ['$scope', function($scope) {
var eq = function(obj) {
return this.id === obj.id;
};
col = [{id: 1, name: 'pizza', equals: eq}, {id:2, name:'unicorns', equals: eq}, {id:3, name:'robots', equals: eq}];
$scope.collection = col;
$scope.my = { favorite : {id:2, name:'unicorns'} };
}]);
请参阅链接。
- 如何在ng-click中传递php对象
- 如何使用ng repeat中的选定输入更新我的对象
- 无法使用ng repeat检索动态创建的JSON对象的属性
- Angular.js通过对象关键点上的ng选项进行跟踪
- 想要将ng个repeat对象传递给自定义筛选函数
- 在禁用ng的情况下搜索JSON对象(AngularJS)
- Angularjs:ng重复对象数组
- 使用ng-src增强对象内部的数组
- AngularJS:根据其他对象预先选择ng repeat中的select元素
- 如果是子对象,则使用Angular设置ng的父对象高度
- 有角度的ng点击ng的内部重复总是得到相同的对象
- 角度控制器不绑定服务对象属性 ng-repeat
- 如何将 JSON 对象映射到 ng-repeat 中的折叠行
- 对象不能在angularjs ng-model中访问
- 在angularjs ng-repeat指令中对对象进行排序
- 将新对象“附加”到 ng 重复项
- 对象内部数组中的角度 ng 重复键
- ng-repeat不绑定ng-model对象数组
- 使用 ng-repeat解析不均匀数据/ json对象
- 在Angular.JS-ng隐藏vs过滤器中将对象分隔为两组