Reputation: 21034
I use a dynamic name attribute technique. I'm using AngularJS verison 1.5.0.
When I run this AngularJS example (code below), which has nested ng-repeats (so I use $index
and $parent.$index
), I expect the correct radio button to be checked when rendered.
Check the screenshot below, the second group of radio buttons are checked, the first group aren't checked.
HTML:
<div id="app">
<div ng-controller="RadioController">
<div ng-repeat="outer_level in outer_levels">
<h1>Outer Level {{ $index }}</h1>
<div ng-repeat="inner_level in outer_level.inner_levels">
<h2>Inner Level {{ $index }}</h2>
<input type="radio"
name="radio_{{$parent.$index}}_{{$index}}"
id="radio_{{$parent.$index}}_{{$index}}"
value="All"
ng-model="inner_level.val" />
<label for="radio_{{$parent.$index}}_{{$index}}"> All</label>
<input type="radio"
name="radio_{{$parent.$index}}_{{$index}}"
id="radio_{{$parent.$index}}_{{$index}}"
value="Any"
ng-model="inner_level.val" />
<label for="radio_{{$index}}"> Any</label>
</div>
</div>
</div>
</div>
Javascript:
window.app = angular
.module('app', [])
.controller('RadioController', function ($scope) {
$scope.message = 'Radio';
$scope.outer_levels = [
{
inner_levels: [
{
val: 'All',
},
{
val: 'Any',
},
],
},
];
});
angular.bootstrap(document.getElementById('app'), ['app']);
The radio button checked attribute is set before the dynamic name attribute resolves.
So all 4 radio buttons are using the same name: radio_{{$parent.$index}}_{{$index}}
Checking the second group of radio buttons, unchecks the first group of radio buttons.
Specifically, in the AngularJS source code, I have a breakpoint in the radioInputType
controller, in its $render
function:
ctrl.$render = function() {
var value = attr.value;
// at this breakpoint, I inspect element[0], and the value of the name attribute is:
// radio_{{$parent.$index}}_{{$index}}
element[0].checked = (value == ctrl.$viewValue);
};
So I have some workarounds:
ng-model
takes care of that... (see above StackBlitz); it unchecks other radio buttons that point to the same ng-model
variable/$viewValue...$error
property, or $valid
state? The $$parentForm
does not reveal its controls
property otherwise I could iterate controls
(without knowing their name)$parent
like $parent.$index
; maybe I could assign a name in advance to the Javascript object, and use that; like inner_level.assigned_name
-- that works<form>
element around each inner_level
, so the radio group names are scoped. StackBlitz here demonstrates that will work.. Suggested in this answer, but might affect your form validation.
<form>
elements cannot be nested, so this solution won't work if you have some outer/root <form>
element; nested <form>
elements are ignored.ng-form
directiveform
, and it is not sufficient; using nested ng-form
elements does not work for the sake of keeping radio buttons separate<fieldset>
element<fieldset>
does not create a new "form scope" or "namespace" keeping the radio buttons separate, so if there is some outer form like a root form, won't work...scope.$watch(_=>element[0].name, _=>ctrl.$render());
attr
object has the correct name from the very first time the radio button directive is attached, but the DOM element[0].name
name attribute isn't updated until later, and unless I add the above "fix", the radio button is not re-rendered at that point...Some things that won't work...
ng-init
strategy to create an alias for the outer $index
(so I have an alias for $parent.$index
), but it doesn't work{{$parent.$index}}
altogether, it works, (which gives insight into how the $render
function fires after the name attributes resolve...) but I don't think that fix is sufficient in a scenario with multiple $parent
levels... the names will be ambiguous (i.e. outer_levels[0]
and outer_levels[1]
would have radio buttons with the same name)Other ideas:
Any other suggestions for me?
Upvotes: 1
Views: 50
Reputation: 56052
Hey the problem was caused due to the interpolation on the id
and name
field, I changed it to ng-id
and ng-name
so that we do not use interpolation, then it started working.
Before:
<input
type="radio"
name="radio_{{$parent.$index}}_{{$index}}"
id="radio_{{$parent.$index}}_{{$index}}"
value="All"
ng-model="inner_level.val"
/>
After:
<input
type="radio"
ng-name="'radio_' + $parent.$index + $index"
ng-id="'radio_' + $parent.$index + $index"
value="All"
ng-model="inner_level.val"
/>
window.app = angular
.module('app', [])
.controller('RadioController', function($scope) {
$scope.message = 'Radio';
$scope.val = 'All'
$scope.outer_levels = [{
inner_levels: [{
val: 'All',
},
{
val: 'Any',
},
{
val: 'Any',
},
],
}, ];
});
angular.bootstrap(document.getElementById('app'), ['app']);
<script src="https://code.angularjs.org/1.5.0/angular.min.js"></script>
<div id="app">
<div ng-controller="RadioController">
<div ng-repeat="outer_level in outer_levels">
<h1>Outer Level {{ $index }}</h1>
<div ng-repeat="inner_level in outer_level.inner_levels">
<h2>Inner Level {{ $index }}</h2>
<input
type="radio"
ng-name="'radio_' + $parent.$index + $index"
ng-id="'radio_' + $parent.$index + $index"
value="All"
ng-model="inner_level.val"
/>
<label for="radio_{{$parent.$index}}_{{$index}}"> All</label>
<input
type="radio"
ng-name="'radio_' + $parent.$index + $index"
ng-id="'radio_' + $parent.$index + $index"
value="Any"
ng-model="inner_level.val"
/>
<label for="radio_{{$index}}"> Any</label>
</div>
</div>
</div>
</div>
Upvotes: 1