Let me start by saying that this question is very similar to issues with selection in a tag using ng-options. For example, Working with select us
OK, so after further review, I decided to go with a more "mix-in" approach, just replacing the ng-model directive with my own custom directive, in essence. This is very similar to the approach I used for making a "checkbox list" directive based on this answer: 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 );
});
}
};
});
As OP requested, here's an example radio button directive that will work with complex objects. It uses underscore.js to find the the selected item from the options. It's a little more complicated than it should be because it also supports loading the options and selected value with AJAX calls.
I write a most simple directive. Using a kind of "track-by" to map two different objects. See the 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;
}
}
};
});
Since I'm not yet able to add comments, so I have to reply here. Dana's answer worked ok for me. Although I'd like to point out in order to use his approach, one would have to implement the 'equals' function on the objects in the collection. See below example:
.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'} };
}]);
See the plunker link.
Why don't you just use the ID for the select like this?
<input type="radio" ng-model="selectedItem" ng-value="item.ID"> {{item.Label}}
And then instead of using selectedItem
you could write items[selectedItem]
.
Oh, and while playing with your problem in jsfiddle I noticed to other things:
a.) You forgot to add a name
attribute to the input.
b.) Don't ever use something without a dot in ng-model. If you actually try to output selectedItem with {{selectedItem}}
outside the ng-repeat block, you will notice that the value does not update when you chose a radio button. This is due to ng-repeat
creating a own child scope.