angularjsradio-buttonangularjs-ng-repeat

AngularJS (1.5.0) nested ng-repeat with radio button not initializing checked properly


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.

screenshot of the initial rendering of this stackblitz https://stackblitz.com/edit/angularjs-rzsjbr?file=index.html -- the second group of radio buttons are checked properly but the first group of radio buttons are not 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:

Some things that won't work...

Other ideas:

Any other suggestions for me?


Solution

  • 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>