1

Please check the code below,

angular.module('myApp', []).controller('myController', ['$scope',
  function($scope) {
    setTimeout(function() {
      $scope.policies = [{
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }, {
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }];
      $scope.$apply();
    }, 1000)

  }
]);
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>

<div ng-app='myApp'>
  <div ng-controller='myController'>
    <div ng-repeat='policy in policies'>
      <div ng-repeat='driver in policy.drivers'>
        Gender : M
        <input type='radio' name='driverGender{{$index}}{{$parent.$index}}' value='M' ng-model='driver.gender' />F
        <input type='radio' ng-model='driver.gender' name='driverGender{{$index}}{{$parent.$index}}' value='F' />
      </div>
    </div>
  </div>
</div>

I was trying to achieve a pair of radio button inside 2 levels of ng-repeat. I can check the radio button, the values changes as expected. But if the values are assigned from control, only last pair of radio button get updated. When I change the version to 1.2.0, it works fine. Please check the sample below

angular.module('myApp', []).controller('myController', ['$scope',
  function($scope) {
    setTimeout(function() {
      $scope.policies = [{
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }, {
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }];
      $scope.$apply();
    }, 1000)

  }
]);
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.0/angular.js"></script>

<div ng-app='myApp'>
  <div ng-controller='myController'>
    <div ng-repeat='policy in policies'>
      <div ng-repeat='driver in policy.drivers'>
        Gender : M
        <input type='radio' name='driverGender{{$index}}{{$parent.$index}}' value='M' ng-model='driver.gender' />F
        <input type='radio' ng-model='driver.gender' name='driverGender{{$index}}{{$parent.$index}}' value='F' />
      </div>
    </div>
  </div>
</div>

Is there something wrong there in the code or any alternative ?

Jithu Rajan
  • 452
  • 3
  • 14
  • don't use `setTimeout` with angular, use `$timeout`. That being said, any kind of timeout in this code makes no sense. – Claies Aug 12 '16 at 13:36
  • Actually my service is returning data. To make it asynchronous I used timeout. Even if it is not there result is same. – Jithu Rajan Aug 12 '16 at 13:40
  • in this sample, you are generating two pairs of radio buttons and assigning all of them to the same model. however, the model isn't defined on the controller, so each iteration of ng-repeat is going to establish it's own copy of the model, which would make changing them from the controller impossible. – Claies Aug 12 '16 at 13:44
  • 'the model isn't defined on the controller'- I believe you are mentioning the model is changed in timeout. Even if I remove the timeout the result is same. Please check - http://plnkr.co/edit/gTmgOPR4jJFM1armkTL2?p=preview – Jithu Rajan Aug 12 '16 at 13:54
  • Good question! I tried for an hour yesterday to figure out what is wrong. The only thing I got that this behaviour was changed in [1.3.0-beta.19](https://github.com/angular/angular.js/compare/v1.3.0-beta.18...v1.3.0-beta.19#diff-a732922b631efed1b9f33a24082ae0db) release. – Shashank Agrawal Aug 13 '16 at 04:26
  • I can't even think about downgrading the framework.. Need to find a solution for this...:( – Jithu Rajan Aug 13 '16 at 04:30

4 Answers4

1

I think the only issue is that there is clash of radio button names, so if you will just replace the radio buttons code with the below one:

    <input type='radio' name='driverGender{{$index}}{{$parent.$index}}0' value='M' ng-model='driver.gender' />F
    <input type='radio' ng-model='driver.gender' name='driverGender{{$index}}{{$parent.$index}}1' value='F' />

Please refer the plnkr for working demo: https://plnkr.co/edit/GKtCRtunNrvLiksbCuXE?p=preview

Harpreet
  • 1,527
  • 13
  • 25
  • This works.But since radio button in a group is having different name, browser will consider it separate group. Default tabbing behavior will not work in this case. – Jithu Rajan Aug 12 '16 at 14:48
1

After personally spending 2-3 hours on this issue, I didn't found out the actual reason for this problem. The only thing that I got is that this thing starts braking in the Angular version 1.3.0-beta.19.

The changelog is 130-beta19-rafter-ascension-2014-08-22.

Note: This answer does not provide you the actual reason for this problem.

To fix this issue, consider removing the name attribute from your input[type=radio] field as the radio works well just with the same ng-model and according to the docs, name is optional as well. https://docs.angularjs.org/api/ng/input/input%5Bradio%5D

angular.module('myApp', []).controller('myController', ['$scope',
  function($scope) {
    setTimeout(function() {
      $scope.policies = [{
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }, {
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }];
      $scope.$apply();
    }, 1000)

  }
]);
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0-beta.19/angular.js"></script>

<div ng-app="myApp" ng-controller="myController">
  {{policies | json}}
  <br><br>

  <div ng-repeat="policy in policies">
    <div ng-repeat="driver in policy.drivers">
      Gender : M
      <input type="radio" value="M" ng-model="driver.gender" /> F
      <input type="radio" ng-model="driver.gender" value="F" />
    </div>
  </div>
</div>

Another solution that seems to working for me. Consider using a directive:

angular.module('myApp', []).controller('myController', ['$scope',
  function($scope) {
    setTimeout(function() {
      $scope.policies = [{
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }, {
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }];
      $scope.$apply();
    }, 1000)

  }
]);

angular.module('myApp').directive('radio', function() {
  return {
    restrict: 'E',
    replace: true,
    template: '<input type="radio" name="{{getFooName()}}" />',
    controller: function($scope) {
      $scope.getFooName = function() {
        return 'driverGender' + $scope.$index +  $scope.$parent.$index;
      }
    }
  }
});
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0-beta.19/angular.js"></script>

<div ng-app="myApp" ng-controller="myController">
  {{policies | json}}
  <br>
  <br>

  <div ng-repeat="policy in policies">
    <div ng-repeat="driver in policy.drivers">
      Gender : M
      <radio ng-model="driver.gender" value="M"></radio>F
      <radio ng-model="driver.gender" value="F"></radio>
    </div>
  </div>
</div>
Shashank Agrawal
  • 25,161
  • 11
  • 89
  • 121
  • If i remove the name attribute, browser doesn't consider radio button M & F as a group. The default tabbing behaviour(https://www.w3.org/TR/2009/WD-wai-aria-practices-20090224/#kbd_general_ex) doesn't work. – Jithu Rajan Aug 13 '16 at 06:19
1

Finally I found a solution.. Instead of 'driverGender{{$index}}{{$parent.$index}}' I created function which return the same.

$scope.getRadioButtonName=function(index,parentIndex){
  return 'gender'+index+parentIndex;
}

Check it below.

// Code goes here
angular.module('myApp', []).controller('myController', ['$scope',
  function($scope) {
   
      $scope.policies = [{
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }, {
        drivers: [{
          gender: 'M'
        }, {
          gender: 'F'
        }]
      }];
     
    $scope.getRadioButtonName=function(index,parentIndex){
      return 'gender'+index+parentIndex;
    }
  }
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>

<div ng-app='myApp'>
  <div ng-controller='myController'>
    <div ng-repeat='policy in policies'>
      <div ng-repeat='driver in policy.drivers'>
        Gender : M
        <input type='radio' ng-model='driver.gender' name='{{getRadioButtonName($index,$parent.$index)}}' value='M' />F
        <input type='radio' ng-model='driver.gender' name='{{getRadioButtonName($index,$parent.$index)}}' value='F' />
      </div>
    </div>
  </div>
</div>
Jithu Rajan
  • 452
  • 3
  • 14
0

You should make a loop and assign the value you want to all of them:

angular.forEach($scope.policies, function(policy) {
   angular.forEach(policy.drivers, function(driver) {
     driver.gender = "M";
   });
});

Hope it helps.

Gustavo Gabriel
  • 1,378
  • 2
  • 17
  • 28
  • I don't understand. The model is already there. Why should I do repeats on that? Could please explain ? – Jithu Rajan Aug 12 '16 at 13:56
  • As @Claies said in his comment. The model is inside the ng-repeat, so each iteration is creating a copy of the model, and the last iteration is the one that is assigning its values to the model, thats why when you change the value in the controller, only the last radio buttons get the new values. I hope it explains a little bit for you. Sorry for bad english... – Gustavo Gabriel Aug 12 '16 at 14:00
  • I dont think so. If I remove the name attributes as Harpreet mentioned it will work. But now tabbing becomes an issue. – Jithu Rajan Aug 12 '16 at 14:52