9

In my AngularJS application, I am displaying contacts data in a grid. My typical contacts JSON looks like as below ...

[
    { type: "IM", value: "mavaze123", default: true },
    { type: "IM", value: "mvaze2014", default: false },
    { type: "IM", value: "mavaze923", default: false },
    { type: "IM", value: "mvaze8927", default: false },
    { type: "Email", value: "mavaze123@abc.com", default: true },
    { type: "Email", value: "mvaze2014@xyz.net", default: false } 
]

The last property 'default' is actually a radio button, selection of which should alter the original default value of the corresponding contact type in above JSON. There can be one default from each type of contact i.e. we can group radio buttons based on the contact type.

<div ng-repeat="contact in contacts">
    <div>{{contact.type}}</div>
    <div>{{contact.value}}</div>
    <div><input type="radio" name="{{contact.type}}" ng-model="contact.default" ng-value="true"/></div>
</div>

Note: The above code is not the exact one, but approximately same, as it will appear inside a custom grid component.

Now when I load my view/edit form page with above JSON, it correctly shows the radio state of all contacts. The problem comes, after page load, when user selects another contact as default. This actually changes the model value of default to true for newly selected contact however the model value of original default contact still remains true, even though its radio state changes to uncheck/blur (because they are having same 'name' value).

I thought to write a directive, but I am unable get it triggered on radio on-blur/uncheck event.

There are various posts on binding boolean values to radio buttons, but I am unable to get it work in my scenario, as I want to update model values for individual radio button in a radio group. See there is no single model representing a radio group.

Original State of radio buttonsModel changes on selection of radio buttonPrevious model remains unchanged on selection of other radio button

Jonas
  • 121,568
  • 97
  • 310
  • 388
mavaze
  • 1,187
  • 3
  • 9
  • 12

3 Answers3

2

I think you should change your design to separate the contacts from contactTypes and store the key to the default contact in contact type.

In your current design, there are duplicated values for default and that's not the desired way to work with radio.

$scope.contacts = [
        { type: "IM", value: "mavaze123" },
        { type: "IM", value: "mvaze2014" },
        { type: "IM", value: "mavaze923" },
        { type: "IM", value: "mvaze8927" },
        { type: "Email", value: "mavaze123@abc.com" },
        { type: "Email", value: "mvaze2014@xyz.net" } 
   ];

    $scope.contactTypes = {
        "IM": { default:"mavaze123"}, //the default is contact with value = mavaze123
        "Email": { default:"mavaze123@abc.com"}
   };

You Html:

<div ng-repeat="contact in contacts">
            <div>{{contact.type}}</div>
            <div>{{contact.value}}</div>
            <div><input type="radio" name="{{contact.type}}" ng-model="contactTypes[contact.type].default" ng-value="contact.value"/></div>
        </div>

DEMO

I assume that the key of contact is value, you could use an Id for your contact.

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • Hi @khanh-to, I tend to accept this as I had already got this working. However I was thinking to operate on the same JSON I am getting from REST response and the same I need to post to REST API after modification. Your solution though works, I need to write backend logic to convert into original JSON format while sending it to REST. I was almost there, as false to true works perfect, though reverse fails on selection of other radio from the same contact type group. – mavaze Jan 04 '14 at 07:23
  • @mavaze: what do you mean by reverse fails? – Khanh TO Jan 04 '14 at 07:31
  • @mavaze: there is no true or false in this design, you assign the key of the default contact to contact type. – Khanh TO Jan 04 '14 at 07:35
  • Hi @khanh-to, I am talking about the solution I achieved so far with the JSON format I have. First, I am able to select only one from each group. Second, if default=false and if I select that radio, the model changes to true, however, third, if I select another radio from the same group, the previously selected radio though changes its state (uncheck/blur), doesn't change its model value to false. – mavaze Jan 04 '14 at 07:44
  • @mavaze: I understand your problem as it was stated in the question. I mean, in the new design, there is no default (true or false) for each contact. We move the default to the contact type instead. Each contact type stores an Id of the default contact. We no longer care about true or false, just care about the `Id` of the default in contact type. – Khanh TO Jan 04 '14 at 07:50
  • @mavaze: take a look at this: http://jsfiddle.net/cJce6/1/ . We are still able to display true, false state if you want. – Khanh TO Jan 04 '14 at 08:03
  • @mavaze You can still use your model. Create `$scope.contactTypes` after you get the data and update the `default` values before you send them back. – a better oliver Jan 04 '14 at 09:23
  • Thanks @khanh-to. As I said in first comment, I accept this as solution. I don't intend to show true and false value (I uploaded some screenshots in my original question), but need to combine your solution with what @ zeroflagL has to say, to achieve the end goal. Though this achieves, I am little unconvinced, but may feel this is the only way. I wish to dig into more. – mavaze Jan 04 '14 at 10:16
  • Please note I am using a custom grid component, so anyway need to send 3 params to grid to match with number of columns. – mavaze Jan 04 '14 at 10:20
1

I added an attribute directive in my input statement ...

<div ng-repeat="contact in contacts">
    <div>{{contact.type}}</div>
    <div>{{contact.value}}</div>
    <div><input type="radio" name="{{contact.type}}" ng-model="contact.default" ng-value="true" boolean-grid-model /></div>
</div>

And my custom directive ...

myModule.directive('booleanGridModel') {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attrs, controller) {
           var radioSelected = scope.$eval(attrs.ngModel);
           if(radioSelected) {
               var selectedContact = scope.contact;
               _.each(scope.contacts, function(contact) {
                  if(contact.type === selectedContact.type) {
                      _.isEqual(contact, selectedContact) ? contact.default = true : contact.default = false;
                  }
               });
           }
       }
    };
}
mavaze
  • 1,187
  • 3
  • 9
  • 12
-1

WHy you declare ng-value="true" please remove that

<div><input type="radio" name="{{contact.type}}" ng-model="contact.default" ng-value="{{contact.default}}"/></div>

Please use $scope.$apply() in your value changing code

Like something below

$scope.$apply(function ChangeType()
{
/Code
});

And you need to change name="{{contact.type}}" to name="contact.type{{$index}}" Because some types are same name.

Ramesh Rajendran
  • 37,412
  • 45
  • 153
  • 234
  • 1
    name="contact.type{{$index}}" doesn't allow selecting one default from each group like one default IM, one default Email, but it may allow to select one default contact across all types. This is not what I want. Also, ng-value="{{contact.default}}" doesn't show the selection of radio button when default is true. With this solution last radio button will be selected always even if default=false. When I set ng-value="true" atleast I ensure the radio is selected only when the default's model value is true. I'm almost there, as false to true work fine, reverse not working though. – mavaze Jan 04 '14 at 07:15